diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 00000000..ad6ef734 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,13 @@ +# Dependabot configuration file. +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "/" + schedule: + interval: "monthly" diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml new file mode 100644 index 00000000..2c071305 --- /dev/null +++ b/.github/workflows/create_release.yml @@ -0,0 +1,16 @@ +name: Create Release +on: + push: + tags: + - '*' +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Create a Release + uses: elgohr/Github-Release-Action@v5 + env: + GITHUB_TOKEN: "${{ secrets.RELEASE_TOKEN }}" + with: + title: ${{ github.ref }} diff --git a/.github/workflows/create_tag.yml b/.github/workflows/create_tag.yml new file mode 100644 index 00000000..c5793f0c --- /dev/null +++ b/.github/workflows/create_tag.yml @@ -0,0 +1,25 @@ +name: Release + +# Runs when a PR merges. +# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#running-your-workflow-when-a-pull-request-merges +on: + pull_request: + types: + - closed + +jobs: + release: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + container: dart + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: master + - uses: jacopocarlini/action-autotag@3.0.0 + with: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 255bcec2..63b8adab 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -1,20 +1,33 @@ -name: Dart CI +name: Dart Checks -on: [push] +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] jobs: build: - - runs-on: ubuntu-latest - - container: - image: google/dart:latest - + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + # Test with at least the declared minimum Dart version + sdk: ['3.5', stable] steps: - - uses: actions/checkout@v1 - - name: Install dependencies - run: pub get - - name: Dart Analyzer - run: dartanalyzer . - - name: Check Dart Format - run: dartfmt -n --set-exit-if-changed . + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 + with: + sdk: ${{ matrix.sdk }} + + - name: Install dependencies + run: dart pub get + - name: Dart Analyzer + run: dart analyze + - name: Check Dart Format + if: ${{ matrix.sdk == 'stable' }} + run: dart format --set-exit-if-changed -onone . + - name: Unit tests + run: dart test + - name: Check if Publishable + run: dart pub publish --dry-run diff --git a/.github/workflows/publish_demos.yml b/.github/workflows/publish_demos.yml new file mode 100644 index 00000000..b6b6169e --- /dev/null +++ b/.github/workflows/publish_demos.yml @@ -0,0 +1,30 @@ +name: Publish Demos +on: + push: + branches: + - master +jobs: + build-and-deploy: + runs-on: ubuntu-latest + container: + image: dart + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v4 + + - name: Install rsync 📚 + run: | + apt-get update && apt-get install -y rsync + + - name: Install and Build 🔧 + run: | + dart pub global activate webdev + dart pub get + dart pub global run webdev build -o build -- --delete-conflicting-outputs + rm build/example/packages + + - name: Publish 🚀 + uses: JamesIves/github-pages-deploy-action@v4.6.1 + with: + branch: gh-pages # The branch the action should deploy to. + folder: build/example # The folder the action should deploy. diff --git a/.github/workflows/publish_pubdev.yml b/.github/workflows/publish_pubdev.yml new file mode 100644 index 00000000..336e0cf5 --- /dev/null +++ b/.github/workflows/publish_pubdev.yml @@ -0,0 +1,14 @@ +# .github/workflows/publish.yml +name: Publish to pub.dev + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+*' + +# Publish using the reusable workflow from dart-lang. +jobs: + publish: + permissions: + id-token: write # Required for authentication using OIDC + uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1 \ No newline at end of file diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml index 1e25d91d..4d51d30c 100644 --- a/.github/workflows/triage.yml +++ b/.github/workflows/triage.yml @@ -8,22 +8,26 @@ jobs: name: Assign Rob runs-on: ubuntu-latest steps: - - uses: actions/checkout@master - - name: Assign Rob - uses: actions/github@v1.0.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v4 + - name: Apply untriaged label + uses: actions/github-script@v7 with: - args: assign @robrbecker - - name: Apply triage label - uses: actions/github@v1.0.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - args: label triage + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['untriaged','unreleased'] + }) - name: Comment On New Issues - uses: actions/github@v1.0.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: actions/github-script@v7 with: - args: comment "Thanks for submitting an issue! @robrbecker will take a look soon!" + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '👋 Thanks for reporting! @robrbecker will take a look.' + }) diff --git a/.pubignore b/.pubignore new file mode 100644 index 00000000..09134047 --- /dev/null +++ b/.pubignore @@ -0,0 +1,3 @@ +tool +test +integration_test \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 44636eb3..8c92ab04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,309 @@ +## 9.25.0 + +* Require Dart 3.5 +* Require `package:http` `^1.0.0`. +* Fix pagination logic to use `next` link. + +## 9.24.0 + +* Bug fixes to the `Issue.isOpen` and `Issue.isClosed` getters. + +## 9.23.0 + +* Require Dart 3.0. +* Update to the latest `package:lints`. + +## 9.22.0 + +* Add support for the `Ghost` user when the Github user is deleted. + +## 9.21.0 + +* Update MiscService.getApiStatus() to use the v2 API + * `APIStatus` has been refactored to match, now exposing `page` and `status` + +## 9.20.0 + +* Add a Changes object to the PullRequestEvent object so we can see what changed in edited PR events by @ricardoamador in https://github.com/SpinlockLabs/github.dart/pull/390 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.19.0...9.20.0 + +## 9.19.0 + +* Revert "Add the 'PushEvent' webhook and associated PushCommit object" by @robrbecker in https://github.com/SpinlockLabs/github.dart/pull/387 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.18.0...9.19.0 + +## 9.18.0 + +- Bad Release. Was: Add the 'PushEvent' webhook and associated PushCommit + +## 9.17.0 + +* Add bearerToken constructor to Authentication class by @kevmoo in https://github.com/SpinlockLabs/github.dart/pull/381 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.16.0...9.17.0 + +## 9.16.0 + +* Fix links and spelling nits in markdown files by @kevmoo in https://github.com/SpinlockLabs/github.dart/pull/379 +* Support latest pkg:http by @kevmoo in https://github.com/SpinlockLabs/github.dart/pull/380 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.15.1...9.16.0 + +## 9.15.1 + +* Revert immutable auth by @CaseyHillers in https://github.com/SpinlockLabs/github.dart/pull/378 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.15.0...9.15.1 + +## 9.15.0 + +* Implement IssuesService.lock/unlock by @Hixie in https://github.com/SpinlockLabs/github.dart/pull/376 +* Bump JamesIves/github-pages-deploy-action from 4.4.1 to 4.4.2 by @dependabot in https://github.com/SpinlockLabs/github.dart/pull/371 +* Make GitHub.auth non-nullable by @CaseyHillers in https://github.com/SpinlockLabs/github.dart/pull/377 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.14.0...9.15.0 + +## 9.14.0 + +* Add optional filter params on Repositories.listCommits by @CaseyHillers in https://github.com/SpinlockLabs/github.dart/pull/368 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.13.0...9.14.0 + +## 9.13.0 + +* Add node_id to the pull request model by @ricardoamador in https://github.com/SpinlockLabs/github.dart/pull/367 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.12.0...9.13.0 + +## 9.12.0 + +* Add support for issue and PR timeline events via `Issue.listTimeline`. + +## 9.11.0 + +* expose IssueLabel.description; update labels REST APIs by @devoncarew in https://github.com/SpinlockLabs/github.dart/pull/355 + +## 9.10.1 + +* Pass required User-Agent HTTP header on all requests + * If `Authentication.basic` is used, it will be your GitHub username/application + * Otherwise, it will default to `github.dart` + +## 9.10.0-dev + +* Require Dart 2.18 +* Expose `CheckSuitesService` and `ChuckRunsService` classes. + +## 9.9.0 + +* Add "author_association" field to the IssueComment object by @ricardoamador in https://github.com/SpinlockLabs/github.dart/pull/348 + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.8.0...9.9.0 + +## 9.8.0 + +* Add "head_branch" field to CheckSuite object by @nehalvpatel in https://github.com/SpinlockLabs/github.dart/pull/347 + +## New Contributors +* @nehalvpatel made their first contribution in https://github.com/SpinlockLabs/github.dart/pull/347 + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.7.0...9.8.0 + +## 9.7.0 +* Add calendar versioning by @CaseyHillers in https://github.com/SpinlockLabs/github.dart/pull/338 + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.6.0...9.7.0 +## 9.6.0 + +* Require Dart 2.17 +* Update to allow different merge methods in pulls_service by @ricardoamador in https://github.com/SpinlockLabs/github.dart/pull/333 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.5.1...9.6.0 + +## 9.5.1 + +* Fix up unit tests & run them in CI by @robrbecker in https://github.com/SpinlockLabs/github.dart/pull/336 + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.5.0...9.5.1 + +## 9.5.0 + +* Add 'commits' member to GitHubComparison object by @fuzzybinary in https://github.com/SpinlockLabs/github.dart/pull/330 + +## New Contributors +* @fuzzybinary made their first contribution in https://github.com/SpinlockLabs/github.dart/pull/330 + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.4.1...9.5.0 + +## 9.4.1 + +* Update to github-script 6 by @robbecker-wf in https://github.com/SpinlockLabs/github.dart/pull/331 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.4.0...9.4.1 + +## 9.4.0 + +* Fix publish release workflow by @CaseyHillers in https://github.com/SpinlockLabs/github.dart/pull/316 +* Add support for toString to the Checkrun object. by @ricardoamador in https://github.com/SpinlockLabs/github.dart/pull/318 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.3.0...9.4.0 + +## 9.3.0 + +* Added a new conclusion state to support flutter autosubmit bot by @ricardoamador in https://github.com/SpinlockLabs/github.dart/pull/315 + +## New Contributors +* @ricardoamador made their first contribution in https://github.com/SpinlockLabs/github.dart/pull/315 + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.2.0...9.3.0 + +## 9.2.0 + +* test auto-release by @robrbecker in https://github.com/SpinlockLabs/github.dart/pull/307 +* test PR for auto-release by @robrbecker in https://github.com/SpinlockLabs/github.dart/pull/308 +* Added assignees to Issue model for #289 by @sjhorn in https://github.com/SpinlockLabs/github.dart/pull/290 + +## New Contributors +* @sjhorn made their first contribution in https://github.com/SpinlockLabs/github.dart/pull/290 + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.1.1...9.2.0 + +## 9.1.1 + +* Don't add state query param twice by @passsy in https://github.com/SpinlockLabs/github.dart/pull/264 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.1.0...9.1.1 + +## 9.1.0 + +* add 'create' github webhook event to hooks.dart by @XilaiZhang in https://github.com/SpinlockLabs/github.dart/pull/304 + +## New Contributors +* @XilaiZhang made their first contribution in https://github.com/SpinlockLabs/github.dart/pull/304 + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.0.3...9.1.0 + +## 9.0.3 + +* Update Language Colors March 13th 2022 by @robrbecker in https://github.com/SpinlockLabs/github.dart/pull/302 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/9.0.2...9.0.3 + +## 9.0.2 +- Switched to use the lints package instead of pedantic https://github.com/SpinlockLabs/github.dart/pull/301 + +## 9.0.1 +- Add `conclusion` property in class `CheckRun` + +## 9.0.0 + +**Breaking change:** In the Gist class, the old type of files was +```dart +List? files; +``` +and the new type is +```dart +Map? files; +``` + +**Breaking change:** In the GistFile class, the name property is now filename + +* Fix getting gists by @robrbecker in https://github.com/SpinlockLabs/github.dart/pull/294 + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/8.5.0...9.0.0 + +## 8.5.0 + +* Adds listing and creating PR Reviews, listing users in an org by @robrbecker in https://github.com/SpinlockLabs/github.dart/pull/287 + + +**Full Changelog**: https://github.com/SpinlockLabs/github.dart/compare/8.4.0...8.5.0 + +## 8.4.0 +- added `updateComment` to update issue comments https://github.com/SpinlockLabs/github.dart/pull/286 + +## 8.3.0 +- Support `files` field in class `GitHubComparison` + +## 8.2.5 +- no library code changes +- Add auto pub publish on new releases + +## 8.2.4 +- Make CheckRunConclusion nullable + +## 8.2.3 +- Added `generateReleaseNotes` boolean to CreateRelase class to have github auto-create release notes +- Added `generateReleaseNotes` method to RepositoriesService to have github create release notes + between to tags (without creating a release) and return the name and body. This is helpful when you want to add the release notes to a CHANGELOG.md before making the actual release +## 8.2.2 +- Up minimum json_serializable to ^6.0.0, json_annotation to ^4.3.0 +- Cleanup and regenerate generated files +- Require Dart SDK 2.14 + +## 8.2.1 +- Add `CheckSuiteEvent` and `CheckRunEvent` + +## 8.2.0 + - add more fields to the PullRequest class and fixed JSON naming bugs + - Added: + - requestedReviewers + - reviewCommentCount + - milestone + - rebaseable + - mergeableState + - maintainerCanModify + - authorAssociation + - Fixed (these were previously always null) + - commentsCount + - commitsCount + - additionsCount + - deletionsCount + - changedFilesCount + +## 8.1.3 + - Add per page parameter to stars related activities https://github.com/SpinlockLabs/github.dart/pull/265 + +## 8.1.2 + - Fixes `RateLimit.fromRateLimitResponse` to not double cast int + +## 8.1.1 + - Fix up examples and license file https://github.com/SpinlockLabs/github.dart/pull/255 https://github.com/SpinlockLabs/github.dart/pull/254 https://github.com/SpinlockLabs/github.dart/pull/253 + +## 8.1.0 + - `RateLimit` queries `/rate_limit` and no longer uses quota + +## 8.0.1 + - Minor tweaks to improve pub score + +## 8.0.0 + - Allow start page, per_page, number of pages options to pagination helper + - Allow page options for listTags + +## 8.0.0-nullsafe.1 + - Update to null safety + +## 7.0.4 + - Add hasPages attribute to Repository https://github.com/SpinlockLabs/github.dart/pull/238 + +## 7.0.3 + - Export `languageColors` as part of the library. This is the map of github languages to their colors https://github.com/SpinlockLabs/github.dart/pull/232 + ## 7.0.2 - https://github.com/SpinlockLabs/github.dart/pull/231 @@ -63,7 +369,7 @@ - For web: browser specific helper methods have moved. use import `package:github/browser_helper.dart` (renderMarkdown, and createAvatorImage) - `createGithubClient(...)` has been removed. Just create a GitHub object directly now. - `findAuthenticationFromEnvironment` now works in both server/flutter and web environments - - On the web, it will check the query string first, then localstorage + - On the web, it will check the query string first, then session storage - all static methods are now factory constructors - fromJSON is now fromJson everywhere - toJSON is now toJson everywhere diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ddf07a0a..b574e5a8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ GitHub.dart is of course Open Source! We love it when people contribute! ## Getting Started - Make sure you have a [GitHub Account](https://github.com/signup/free). -- Make sure the [Dart SDK](https://www.dartlang.org/tools/sdk/) is installed on your system. +- Make sure the [Dart SDK](https://dart.dev/tools/sdk) is installed on your system. - Make sure you have [Git](http://git-scm.com/) installed on your system. - [Fork](https://help.github.com/articles/fork-a-repo) the [repository](https://github.com/SpinlockLabs/github.dart) on GitHub. @@ -15,12 +15,12 @@ GitHub.dart is of course Open Source! We love it when people contribute! - [Commit your code](http://git-scm.com/book/en/Git-Basics-Recording-Changes-to-the-Repository) for each logical change (see [tips for creating better commit messages](http://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message)). - [Push your change](https://help.github.com/articles/pushing-to-a-remote) to your fork. - [Create a Pull Request](https://help.github.com/articles/creating-a-pull-request) on GitHub for your change. -- Wait for reviewers (usually kaendfinger) to give feedback. +- Wait for reviewers (usually robrbecker) to give feedback. - When the reviewers think that the Pull Request is ready, they will merge it. ## Code Style -GitHub.dart follows the [Dart Style Guide](https://www.dartlang.org/articles/style-guide/). Please note that if your code is not formatted according to the guide as much as possible, we will reject your Pull Request until it is fixed. Some things such as long lines will generally be accepted, however try to make it smaller if possible. +GitHub.dart follows the [Dart Style Guide](https://dart.dev/effective-dart/style). Please note that if your code is not formatted according to the guide as much as possible, we will reject your Pull Request until it is fixed. Some things such as long lines will generally be accepted, however try to make it smaller if possible. ## Efficiency @@ -28,13 +28,38 @@ GitHub.dart is committed to efficiency as much as possible. If your code is not ## Rejections -Pull Request rejections are not a bad thing. It just means you need to fix something. Perhaps it is important to define 'rejection' as it is used in this case. A rejection is when a GitHub.dart committer comments on a Pull Request with a comment like 'rejected due to incorrect formatting'. +Pull Request rejections are not a bad thing. It just means you need to fix something. Perhaps it is important to define 'rejection' as it is used in this case. A rejection is when a `GitHub.dart` committer comments on a Pull Request with a comment like 'rejected due to incorrect formatting'. + +## Generated code + +To regenerate the JSON logic for the models, run: + +```sh +dart run build_runner build -d +``` + +## Tests + +`dart test` will only run the unit tests. + +To run the complete test suite you will need to install +`octokit/fixtures-server`. + +``` +npm install --global @octokit/fixtures-server +``` + +Tests can be run using `make test`, which will start up a local mock +GitHub and execute tests against it using your localhost port 3000. ## Contacting Us -- IRC: `#directcode on irc.esper.net and irc.freenode.net` -- Email: `kaendfinger@gmail.com` +File issues at https://github.com/SpinlockLabs/github.dart/issues + +## Releases -## Becoming a Committer +Merged pull requests that edit the `pubspec.yaml` version will create new releases. +Once CI is green, it will create a tag for that commit based on the version, which +gets published by pub.dev. -If you get on IRC and ask us, we can review your work and add you as a committer if we think you should have it. +If no new version was created, nothing will be published. diff --git a/LICENSE b/LICENSE index 838d36d8..e4f06dbe 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,3 @@ -``` The MIT License (MIT) Copyright (c) 2014 DirectCode @@ -20,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -``` diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..c146167b --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +.DEFAULT_GOAL := help +SHELL=/bin/bash -o pipefail + +# Cite: https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html +.PHONY: help +help: ## Display this help page + @grep -E '^[a-zA-Z0-9/_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: fixtures +fixtures: ## Run octokit-fixtures-server for scenario tests + @npx octokit-fixtures-server & + +.PHONY: stop +stop: ## Stop the fixtures server + @killall node + +.PHONY: test +test: fixtures ## Run tests + @dart test -P all + make stop \ No newline at end of file diff --git a/README.md b/README.md index f6f55a12..08dc8e09 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,27 @@ # GitHub for Dart -![](https://github.com/SpinlockLabs/github.dart/workflows/Dart%20CI/badge.svg) -[![Pub](https://img.shields.io/pub/v/github.svg)](https://pub.dartlang.org/packages/github) +[![Dart Checks](https://github.com/SpinlockLabs/github.dart/actions/workflows/dart.yml/badge.svg)](https://github.com/SpinlockLabs/github.dart/actions/workflows/dart.yml) +[![Pub](https://img.shields.io/pub/v/github.svg)](https://pub.dev/packages/github) This is a library for interacting with GitHub in Dart. It works on all platforms including web, server, and Flutter. Please submit issues and pull requests, help out, or just give encouragement. -**Notice**: We are looking for contributors. If you're interested or have questions, join the chat at https://gitter.im/SpinlockLabs/community +**Notice**: This is not an official GitHub project. It is maintained by volunteers. +We are looking for contributors. If you're interested or have questions, head over to discussions https://github.com/SpinlockLabs/github.dart/discussions ## Features - Works on the Server, Browser, and Flutter - Really Fast -- Plugable API +- Pluggable API - Supports Authentication - Builtin OAuth2 Flow - Hook Server Helper ## Links -- [Library Demos](https://github.directcode.org/demos/) -- [Pub Package](https://pub.dartlang.org/packages/github) +- [Library Demos](https://spinlocklabs.github.io/github.dart/) (based on the [sample code](https://github.com/SpinlockLabs/github.dart/tree/master/example)) +- [Pub Package](https://pub.dev/packages/github) - [Wiki](https://github.com/SpinlockLabs/github.dart/wiki) - [Latest API reference](https://pub.dev/documentation/github/latest/) @@ -30,4 +31,8 @@ See the examples in the example directory to learn how to use some of the featur ## Contacting Us -Join our Gitter chat at https://gitter.im/SpinlockLabs/community +Post a question or idea: https://github.com/SpinlockLabs/github.dart/discussions + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=SpinlockLabs/github.dart&type=Date)](https://star-history.com/#SpinlockLabs/github.dart&Date) diff --git a/analysis_options.yaml b/analysis_options.yaml index 442b7c0c..f2974469 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,397 +1,58 @@ -include: package:pedantic/analysis_options.yaml +include: package:lints/recommended.yaml analyzer: - strong-mode: - implicit-casts: true - implicit-dynamic: true + language: + #strict-casts: true linter: rules: - - # Separate the control structure expression from its statement. - # http://dart-lang.github.io/linter/lints/always_put_control_body_on_new_line.html - always_put_control_body_on_new_line - - # Put @required named parameters first. - # http://dart-lang.github.io/linter/lints/always_put_required_named_parameters_first.html - always_put_required_named_parameters_first - - # Avoid bool literals in conditional expressions. - # http://dart-lang.github.io/linter/lints/avoid_bool_literals_in_conditional_expressions.html - # - avoid_bool_literals_in_conditional_expressions - - # Don't explicitly catch Error or types that implement it. - # http://dart-lang.github.io/linter/lints/avoid_catching_errors.html - avoid_catching_errors - - # Avoid defining a class that contains only static members. - # http://dart-lang.github.io/linter/lints/avoid_classes_with_only_static_members.html - avoid_classes_with_only_static_members - - # Avoid double and int checks. - # http://dart-lang.github.io/linter/lints/avoid_double_and_int_checks.html - avoid_double_and_int_checks - - # Avoid field initializers in const classes. - # http://dart-lang.github.io/linter/lints/avoid_field_initializers_in_const_classes.html - avoid_field_initializers_in_const_classes - - # Avoid using `forEach` with a function literal. - # http://dart-lang.github.io/linter/lints/avoid_function_literals_in_foreach_calls.html - # reason: Use for (x in y) or forEach(someFunc) instead - # - avoid_function_literals_in_foreach_calls - - # Don't implement classes that override `==`. - # http://dart-lang.github.io/linter/lints/avoid_implementing_value_types.html - avoid_implementing_value_types - - # Avoid JavaScript rounded ints. - # http://dart-lang.github.io/linter/lints/avoid_js_rounded_ints.html - avoid_js_rounded_ints - - # Avoid positional boolean parameters. - # http://dart-lang.github.io/linter/lints/avoid_positional_boolean_parameters.html - # - avoid_positional_boolean_parameters - - # Avoid `print` calls in production code. - # http://dart-lang.github.io/linter/lints/avoid_print.html - # - avoid_print - - # Avoid private typedef functions. - # http://dart-lang.github.io/linter/lints/avoid_private_typedef_functions.html - avoid_private_typedef_functions - - # Don't rename parameters of overridden methods. - # http://dart-lang.github.io/linter/lints/avoid_renaming_method_parameters.html - # - avoid_renaming_method_parameters - - # Avoid returning null from members whose return type is bool, double, int, or num. - # http://dart-lang.github.io/linter/lints/avoid_returning_null.html - - avoid_returning_null - - # Avoid returning null for Future. - # http://dart-lang.github.io/linter/lints/avoid_returning_null_for_future.html - - avoid_returning_null_for_future - - # Avoid returning null for void. - # http://dart-lang.github.io/linter/lints/avoid_returning_null_for_void.html - - avoid_returning_null_for_void - - # Avoid returning this from methods just to enable a fluent interface. - # http://dart-lang.github.io/linter/lints/avoid_returning_this.html - avoid_returning_this - - # Avoid setters without getters. - # http://dart-lang.github.io/linter/lints/avoid_setters_without_getters.html - avoid_setters_without_getters - - # Avoid single cascade in expression statements. - # http://dart-lang.github.io/linter/lints/avoid_single_cascade_in_expression_statements.html - - avoid_single_cascade_in_expression_statements - - # Avoid slow async `dart:io` methods. - # http://dart-lang.github.io/linter/lints/avoid_slow_async_io.html - avoid_slow_async_io - - # Avoid defining unused parameters in constructors. - # http://dart-lang.github.io/linter/lints/avoid_unused_constructor_parameters.html - avoid_unused_constructor_parameters - - # Avoid async functions that return void. - # http://dart-lang.github.io/linter/lints/avoid_void_async.html - avoid_void_async - - # Await only futures. - # http://dart-lang.github.io/linter/lints/await_only_futures.html - - await_only_futures - - # Name types using UpperCamelCase. - # http://dart-lang.github.io/linter/lints/camel_case_types.html - - camel_case_types - - # Cancel instances of dart.async.StreamSubscription. - # http://dart-lang.github.io/linter/lints/cancel_subscriptions.html - cancel_subscriptions - - # Cascade consecutive method invocations on the same reference. - # http://dart-lang.github.io/linter/lints/cascade_invocations.html - # - cascade_invocations - - # Close instances of `dart.core.Sink`. - # http://dart-lang.github.io/linter/lints/close_sinks.html - close_sinks - - # Only reference in scope identifiers in doc comments. - # http://dart-lang.github.io/linter/lints/comment_references.html - comment_references - - # Avoid control flow in finally blocks. - # http://dart-lang.github.io/linter/lints/control_flow_in_finally.html - - control_flow_in_finally - - # DO reference all public properties in debug methods. - # http://dart-lang.github.io/linter/lints/diagnostic_describe_all_properties.html - diagnostic_describe_all_properties - - # Adhere to Effective Dart Guide directives sorting conventions. - # http://dart-lang.github.io/linter/lints/directives_ordering.html - directives_ordering - - # Avoid empty statements. - # http://dart-lang.github.io/linter/lints/empty_statements.html - - empty_statements - - # Name source files using `lowercase_with_underscores`. - # http://dart-lang.github.io/linter/lints/file_names.html - - file_names - - # Use Flutter TODO format: // TODO(username): message, https://URL-to-issue. - # http://dart-lang.github.io/linter/lints/flutter_style_todos.html - # - flutter_style_todos - - # Always override `hashCode` if overriding `==`. - # http://dart-lang.github.io/linter/lints/hash_and_equals.html - - hash_and_equals - - # Don't import implementation files from another package. - # http://dart-lang.github.io/linter/lints/implementation_imports.html - - implementation_imports - - # Conditions should not unconditionally evaluate to `true` or to `false`. - # http://dart-lang.github.io/linter/lints/invariant_booleans.html - # reason: There are several outstanding bugs with this lint that cause a good deal of noise - - invariant_booleans - - # Invocation of Iterable.contains with references of unrelated types. - # http://dart-lang.github.io/linter/lints/iterable_contains_unrelated_type.html - - iterable_contains_unrelated_type - - # Join return statement with assignment when possible. - # http://dart-lang.github.io/linter/lints/join_return_with_assignment.html - join_return_with_assignment - - - # AVOID lines longer than 80 characters. - # http://dart-lang.github.io/linter/lints/lines_longer_than_80_chars.html - # - lines_longer_than_80_chars - - # Invocation of `remove` with references of unrelated types. - # http://dart-lang.github.io/linter/lints/list_remove_unrelated_type.html - - list_remove_unrelated_type - - # Boolean expression composed only with literals. - # http://dart-lang.github.io/linter/lints/literal_only_boolean_expressions.html - literal_only_boolean_expressions - - # Don't use adjacent strings in list. - # http://dart-lang.github.io/linter/lints/no_adjacent_strings_in_list.html - no_adjacent_strings_in_list - - - # Name non-constant identifiers using lowerCamelCase. - # http://dart-lang.github.io/linter/lints/non_constant_identifier_names.html - - non_constant_identifier_names - - # Avoid defining a one-member abstract class when a simple function will do. - # http://dart-lang.github.io/linter/lints/one_member_abstracts.html + - omit_local_variable_types - one_member_abstracts - - # Only throw instances of classes extending either Exception or Error. - # http://dart-lang.github.io/linter/lints/only_throw_errors.html - only_throw_errors - - # Don't override fields. - # http://dart-lang.github.io/linter/lints/overridden_fields.html - - overridden_fields - - # Provide doc comments for all public APIs. - # http://dart-lang.github.io/linter/lints/package_api_docs.html - - package_api_docs - - # Use `lowercase_with_underscores` for package names. - # http://dart-lang.github.io/linter/lints/package_names.html - - package_names - - # Prefix library names with the package name and a dot-separated path. - # http://dart-lang.github.io/linter/lints/package_prefixed_library_names.html - - package_prefixed_library_names - - # Don't reassign references to parameters of functions or methods. - # http://dart-lang.github.io/linter/lints/parameter_assignments.html - # - parameter_assignments - - # Prefer putting asserts in initializer list. - # http://dart-lang.github.io/linter/lints/prefer_asserts_in_initializer_lists.html - prefer_asserts_in_initializer_lists - - # Prefer asserts with message. - # http://dart-lang.github.io/linter/lints/prefer_asserts_with_message.html - # - prefer_asserts_with_message - - # Prefer using a boolean as the assert condition. - # http://dart-lang.github.io/linter/lints/prefer_bool_in_asserts.html - # reason: This lint rule has been deprecated - # - prefer_bool_in_asserts - - # Prefer using `??=` over testing for null. - # http://dart-lang.github.io/linter/lints/prefer_conditional_assignment.html - # - prefer_conditional_assignment - - # Prefer const with constant constructors. - # http://dart-lang.github.io/linter/lints/prefer_const_constructors.html - prefer_const_constructors - - # Prefer declare const constructors on `@immutable` classes. - # http://dart-lang.github.io/linter/lints/prefer_const_constructors_in_immutables.html - prefer_const_constructors_in_immutables - - # Prefer const over final for declarations. - # http://dart-lang.github.io/linter/lints/prefer_const_declarations.html - prefer_const_declarations - - # Prefer const literals as parameters of constructors on @immutable classes. - # http://dart-lang.github.io/linter/lints/prefer_const_literals_to_create_immutables.html - prefer_const_literals_to_create_immutables - - # Prefer defining constructors instead of static methods to create instances. - # http://dart-lang.github.io/linter/lints/prefer_constructors_over_static_methods.html - prefer_constructors_over_static_methods - - # Prefer final in for-each loop variable if reference is not reassigned. - # http://dart-lang.github.io/linter/lints/prefer_final_in_for_each.html - prefer_final_in_for_each - - # Use `forEach` to only apply a function to all the elements. - # http://dart-lang.github.io/linter/lints/prefer_foreach.html - prefer_foreach - - # Use a function declaration to bind a function to a name. - # http://dart-lang.github.io/linter/lints/prefer_function_declarations_over_variables.html - - prefer_function_declarations_over_variables - - # Prefer if elements to conditional expressions where possible. - # http://dart-lang.github.io/linter/lints/prefer_if_elements_to_conditional_expressions.html - prefer_if_elements_to_conditional_expressions - - # Use initializing formals when possible. - # http://dart-lang.github.io/linter/lints/prefer_initializing_formals.html - - prefer_initializing_formals - - # Inline list item declarations where possible. - # http://dart-lang.github.io/linter/lints/prefer_inlined_adds.html - - prefer_inlined_adds - - # Prefer int literals over double literals. - # http://dart-lang.github.io/linter/lints/prefer_int_literals.html - prefer_int_literals - - # Prefer using mixins. - # http://dart-lang.github.io/linter/lints/prefer_mixin.html - prefer_mixin - - # Prefer typing uninitialized variables and fields. - # http://dart-lang.github.io/linter/lints/prefer_typing_uninitialized_variables.html - - prefer_typing_uninitialized_variables - - # Don't use the Null type, unless you are positive that you don't want void. - # http://dart-lang.github.io/linter/lints/prefer_void_to_null.html - - prefer_void_to_null - - # Provide a deprecation message, via @Deprecated("message"). - # http://dart-lang.github.io/linter/lints/provide_deprecation_message.html - - provide_deprecation_message - - # Document all public members. - # http://dart-lang.github.io/linter/lints/public_member_api_docs.html - # reason: Can get annoying for React component lifecycle methods, constructors. - # - public_member_api_docs - - # Sort child properties last in widget instance creations. - # http://dart-lang.github.io/linter/lints/sort_child_properties_last.html - sort_child_properties_last - - # Sort constructor declarations before other members. - # http://dart-lang.github.io/linter/lints/sort_constructors_first.html - # - sort_constructors_first - - # Sort pub dependencies. - # http://dart-lang.github.io/linter/lints/sort_pub_dependencies.html - sort_pub_dependencies - - # Sort unnamed constructor declarations first. - # http://dart-lang.github.io/linter/lints/sort_unnamed_constructors_first.html - sort_unnamed_constructors_first - - # Test type arguments in operator ==(Object other). - # http://dart-lang.github.io/linter/lints/test_types_in_equals.html - test_types_in_equals - - # Avoid `throw` in finally block. - # http://dart-lang.github.io/linter/lints/throw_in_finally.html - throw_in_finally - - # Type annotate public APIs. - # http://dart-lang.github.io/linter/lints/type_annotate_public_apis.html - # reason: React component render() method can return either ReactElement or false. Use overrides. - type_annotate_public_apis - - # Unnecessary await keyword in return. - # http://dart-lang.github.io/linter/lints/unnecessary_await_in_return.html - unnecessary_await_in_return - - # Avoid using braces in interpolation when not needed. - # http://dart-lang.github.io/linter/lints/unnecessary_brace_in_string_interps.html - - unnecessary_brace_in_string_interps - - # Avoid wrapping fields in getters and setters just to be "safe". - # http://dart-lang.github.io/linter/lints/unnecessary_getters_setters.html - - unnecessary_getters_setters - - # Don't create a lambda when a tear-off will do. - # http://dart-lang.github.io/linter/lints/unnecessary_lambdas.html - unnecessary_lambdas - - # Avoid null in null-aware assignment. - # http://dart-lang.github.io/linter/lints/unnecessary_null_aware_assignments.html - - unnecessary_null_aware_assignments - - # Don't override a method to do a super method invocation with the same parameters. - # http://dart-lang.github.io/linter/lints/unnecessary_overrides.html - - unnecessary_overrides - - # Unnecessary parenthesis can be removed. - # http://dart-lang.github.io/linter/lints/unnecessary_parenthesis.html - unnecessary_parenthesis - - # Avoid using unnecessary statements. - # http://dart-lang.github.io/linter/lints/unnecessary_statements.html - unnecessary_statements - - # Avoid unsafe HTML APIs. - # http://dart-lang.github.io/linter/lints/unsafe_html.html - # - unsafe_html - - # Prefer an 8-digit hexadecimal integer(0xFFFFFFFF) to instantiate Color. - # http://dart-lang.github.io/linter/lints/use_full_hex_values_for_flutter_colors.html - use_full_hex_values_for_flutter_colors - - # Use a setter for operations that conceptually change a property. - # http://dart-lang.github.io/linter/lints/use_setters_to_change_properties.html - use_setters_to_change_properties - - # Use string buffers to compose strings. - # http://dart-lang.github.io/linter/lints/use_string_buffers.html - use_string_buffers - - # Start the name of the method with to/_to or as/_as if applicable. - # http://dart-lang.github.io/linter/lints/use_to_and_as_if_applicable.html - use_to_and_as_if_applicable - - # Don't assign to void. - # http://dart-lang.github.io/linter/lints/void_checks.html - # reason: Trying to assigning a value to void is an error. - - void_checks - - - omit_local_variable_types - - prefer_final_fields - - use_function_type_syntax_for_parameters diff --git a/build.yaml b/build.yaml new file mode 100644 index 00000000..fdbbd17d --- /dev/null +++ b/build.yaml @@ -0,0 +1,8 @@ +targets: + $default: + builders: + json_serializable: + options: + # Options configure how source code is generated for every + # `@JsonSerializable`-annotated class in the package. + field_rename: snake diff --git a/dart_test.yaml b/dart_test.yaml new file mode 100644 index 00000000..04f3491b --- /dev/null +++ b/dart_test.yaml @@ -0,0 +1,15 @@ +tags: + scenarios: + skip: | + Not run by default when running dart test. To run: + npx octokit-fixtures-server + dart test -P scenarios + or run all tests with: + make test + +presets: + scenarios: + include_tags: scenarios + run_skipped: true + all: + run_skipped: true \ No newline at end of file diff --git a/example/common.dart b/example/common.dart index 27a3c7f3..e508d0ee 100644 --- a/example/common.dart +++ b/example/common.dart @@ -1,10 +1,11 @@ import 'dart:async'; +// ignore: deprecated_member_use import 'dart:html'; import 'package:github/github.dart'; -export 'package:github/github.dart'; export 'package:github/browser_helper.dart'; +export 'package:github/github.dart'; /// Wires up a listener to a button with an id of view-source, /// if it exists, to show the script source @@ -12,9 +13,11 @@ export 'package:github/browser_helper.dart'; /// view source button, then you don't need to call this method Future initViewSourceButton(String script) async { // query the DOM for the view source button, handle clicks - document.querySelector('#view-source')?.onClick?.listen((_) { - final popup = window.open('view_source.html?script=$script', 'View Source'); - String code; + document.querySelector('#view-source')?.onClick.listen((_) { + final popup = window.open( + 'https://github.com/SpinlockLabs/github.dart/blob/master/example/$script', + 'View Source'); + String? code; var fetched = false; var ready = false; diff --git a/example/emoji.dart b/example/emoji.dart index f2802c47..663269dd 100644 --- a/example/emoji.dart +++ b/example/emoji.dart @@ -1,9 +1,10 @@ import 'dart:async'; +// ignore: deprecated_member_use import 'dart:html'; import 'common.dart'; -Element emojiDiv; +Element? emojiDiv; Future main() async { await initViewSourceButton('emoji.dart'); @@ -25,23 +26,23 @@ Future loadEmojis() async { h.append( ImageElement(src: url, width: 64, height: 64)..classes.add('emoji')); h.append(ParagraphElement()..text = ':$name:'); - emojiDiv.append(h); + emojiDiv!.append(h); }); } -String lastQuery; +String? lastQuery; -void filter(String query) { +void filter(String? query) { if (lastQuery != null && lastQuery == query) { return; } lastQuery = query; - final boxes = emojiDiv.children; + final boxes = emojiDiv!.children; for (final box in boxes) { - final boxName = box.querySelector('p'); - final t = boxName.text; + final boxName = box.querySelector('p')!; + final t = boxName.text!; final name = t.substring(1, t.length - 1); - if (name.contains(query)) { + if (name.contains(query!)) { box.style.display = 'inline'; } else { box.style.display = 'none'; diff --git a/example/gist.dart b/example/gist.dart new file mode 100755 index 00000000..0541ba58 --- /dev/null +++ b/example/gist.dart @@ -0,0 +1,7 @@ +import 'package:github/github.dart'; + +Future main() async { + final github = GitHub(auth: findAuthenticationFromEnvironment()); + var g = await github.gists.getGist('c14da36c866b9fe6f84f5d774b76570b'); + print(g.files); +} diff --git a/example/gist.html b/example/gist.html new file mode 100755 index 00000000..e25fd0bb --- /dev/null +++ b/example/gist.html @@ -0,0 +1,36 @@ + + + + + + + + Gist + + + + +

Gist

+ + +

+ +
+

+
+
+ + + + + + \ No newline at end of file diff --git a/example/index.dart b/example/index.dart index e1dec24a..535acd07 100644 --- a/example/index.dart +++ b/example/index.dart @@ -1,11 +1,13 @@ +// ignore: deprecated_member_use import 'dart:html'; + import 'common.dart'; void main() { - final InputElement tokenInput = querySelector('#token'); + final tokenInput = querySelector('#token') as InputElement; tokenInput.value = github.auth.token ?? ''; - window.sessionStorage['GITHUB_TOKEN'] = tokenInput.value; + window.sessionStorage['GITHUB_TOKEN'] = tokenInput.value!; tokenInput.onKeyUp.listen((_) { - window.sessionStorage['GITHUB_TOKEN'] = tokenInput.value; + window.sessionStorage['GITHUB_TOKEN'] = tokenInput.value!; }); } diff --git a/example/index.html b/example/index.html index a2ee2782..81e054e8 100644 --- a/example/index.html +++ b/example/index.html @@ -34,6 +34,7 @@

GitHub for Dart - Demos

User Information Language Breakdown Releases + Pull Request Stars Code Search Emoji diff --git a/example/languages.dart b/example/languages.dart index e8d657df..aa8b7ec1 100644 --- a/example/languages.dart +++ b/example/languages.dart @@ -1,33 +1,24 @@ +// ignore: deprecated_member_use import 'dart:html'; -import 'package:github/github.dart'; import 'common.dart'; -DivElement tableDiv; +DivElement? tableDiv; -LanguageBreakdown breakdown; +late LanguageBreakdown breakdown; Future main() async { await initViewSourceButton('languages.dart'); - tableDiv = querySelector('#table'); + tableDiv = querySelector('#table') as DivElement?; await loadRepository(); } Future loadRepository() async { - var user = 'dart-lang'; - var reponame = 'sdk'; - final params = queryString; + var user = params['user'] ?? 'dart-lang'; + var reponame = params['repo'] ?? 'sdk'; - if (params.containsKey('user')) { - user = params['user']; - } - - if (params.containsKey('repo')) { - reponame = params['repo']; - } - - document.getElementById('name').setInnerHtml('$user/$reponame'); + document.getElementById('name')!.text = '$user/$reponame'; final repo = RepositorySlug(user, reponame); breakdown = await github.repositories.listLanguages(repo); @@ -44,7 +35,7 @@ void reloadTable({int accuracy = 4}) { isReloadingTable = true; final md = generateMarkdown(accuracy); github.misc.renderMarkdown(md).then((html) { - tableDiv.setInnerHtml(html, treeSanitizer: NodeTreeSanitizer.trusted); + tableDiv!.setInnerHtml(html, treeSanitizer: NodeTreeSanitizer.trusted); isReloadingTable = false; }); } @@ -57,15 +48,17 @@ String generateMarkdown(int accuracy) { final total = totalBytes(breakdown); final data = breakdown.toList(); - var md = '|Name|Bytes|Percentage|\n'; - md += '|-----|-----|-----|\n'; + var md = StringBuffer(''' +|Name|Bytes|Percentage| +|-----|-----|-----| +'''); data.sort((a, b) => b[1].compareTo(a[1])); - data.forEach((info) { - final String name = info[0]; + for (final info in data) { + final String? name = info[0]; final int bytes = info[1]; final num percentage = (bytes / total) * 100; - md += '|$name|$bytes|${percentage.toStringAsFixed(accuracy)}|\n'; - }); - return md; + md.writeln('|$name|$bytes|${percentage.toStringAsFixed(accuracy)}|'); + } + return md.toString(); } diff --git a/example/organization.dart b/example/organization.dart index 178b2041..d11d0fdd 100644 --- a/example/organization.dart +++ b/example/organization.dart @@ -1,28 +1,28 @@ import 'dart:async'; +// ignore: deprecated_member_use import 'dart:html'; -import 'package:github/github.dart'; import 'common.dart'; -DivElement $output; -InputElement $input; -ButtonElement $btn; +DivElement? $output; +InputElement? $input; +ButtonElement? $btn; Future main() async { await initViewSourceButton('organization.dart'); - $output = querySelector('#output'); - $input = querySelector('#input'); - $btn = querySelector('#submit'); - $input.onChange.listen((_) { - loadOrganization($input.value); + $output = querySelector('#output') as DivElement?; + $input = querySelector('#input') as InputElement?; + $btn = querySelector('#submit') as ButtonElement?; + $input!.onChange.listen((_) { + loadOrganization($input!.value); }); - $btn.onClick.listen((_) { - loadOrganization($input.value); + $btn!.onClick.listen((_) { + loadOrganization($input!.value); }); - $btn.click(); + $btn!.click(); } -Future loadOrganization(String orgToLoad) async { +Future loadOrganization(String? orgToLoad) async { try { final org = await github.organizations.get(orgToLoad); final html = ''' @@ -32,8 +32,8 @@ Future loadOrganization(String orgToLoad) async {
Followers: ${org.followersCount}
Following: ${org.followingCount} '''; - $output.innerHtml = html; + $output!.innerHtml = html; } on OrganizationNotFound { - $output.innerHtml = 'Not found.'; + $output!.innerHtml = 'Not found.'; } } diff --git a/example/pr.dart b/example/pr.dart new file mode 100644 index 00000000..660d4c45 --- /dev/null +++ b/example/pr.dart @@ -0,0 +1,17 @@ +import 'dart:async'; +// ignore: deprecated_member_use +import 'dart:html'; + +import 'common.dart'; + +Future main() async { + await initViewSourceButton('pr.dart'); + var pr = await github.pullRequests + .get(RepositorySlug('flutter', 'flutter'), 90295); + renderPr(pr); +} + +void renderPr(PullRequest pr) { + var prDiv = querySelector('#pr')!; + prDiv.innerText = pr.toJson().toString(); +} diff --git a/example/pr.html b/example/pr.html new file mode 100644 index 00000000..d9973493 --- /dev/null +++ b/example/pr.html @@ -0,0 +1,23 @@ + + + + + GitHub for Dart - Pull Request + + + +
+

GitHub for Dart - Pull Request

+ + +

+
+ + Pull Request JSON: +

+
+  
+
+
+
+
\ No newline at end of file
diff --git a/example/readme.dart b/example/readme.dart
index fa572b52..1920cca4 100644
--- a/example/readme.dart
+++ b/example/readme.dart
@@ -1,22 +1,13 @@
-import 'dart:convert';
+// ignore: deprecated_member_use
 import 'dart:html';
 
-import 'package:github/github.dart';
-
 import 'common.dart';
 
-DivElement readmeDiv;
-
 Future main() async {
   await initViewSourceButton('readme.dart');
-  readmeDiv = querySelector('#readme');
+  var readmeDiv = querySelector('#readme')!;
   var repo = RepositorySlug('SpinlockLabs', 'github.dart');
   final readme = await github.repositories.getReadme(repo);
-  var markdown = readme.content;
-  if (readme.encoding == 'base64') {
-    markdown = String.fromCharCodes(base64.decode(markdown));
-  }
-  print(markdown);
-  final html = await github.misc.renderMarkdown(markdown);
+  final html = await github.misc.renderMarkdown(readme.text);
   readmeDiv.appendHtml(html, treeSanitizer: NodeTreeSanitizer.trusted);
 }
diff --git a/example/release_notes.dart b/example/release_notes.dart
new file mode 100644
index 00000000..27835cdd
--- /dev/null
+++ b/example/release_notes.dart
@@ -0,0 +1,63 @@
+// ignore: deprecated_member_use
+import 'dart:html';
+
+import 'package:pub_semver/pub_semver.dart';
+
+import 'common.dart';
+
+late DivElement releasesDiv;
+
+Future main() async {
+  await initViewSourceButton('release_notes.dart');
+  releasesDiv = querySelector('#release_notes')! as DivElement;
+  releasesDiv.innerText = await loadReleaseNotes();
+}
+
+Future loadReleaseNotes() async {
+  var slug = RepositorySlug.full('robrbecker/experiment');
+  // var slug = RepositorySlug.full('SpinlockLabs/github.dart');
+
+  var latestRelease = await github.repositories.getLatestRelease(slug);
+  var latestTag = latestRelease.tagName!;
+  var latestVersion = Version.parse(latestTag);
+
+  var unreleasedPRs = await github.search
+      .issues(
+          'repo:${slug.fullName} is:pull-request label:unreleased state:closed',
+          sort: 'desc')
+      .toList();
+  if (unreleasedPRs.isEmpty) {
+    print('No unreleased PRs');
+    return '';
+  }
+  var semvers = {};
+  for (final pr in unreleasedPRs) {
+    var prlabels = pr.labels
+        .where((element) => element.name.startsWith('semver:'))
+        .toList();
+    for (final l in prlabels) {
+      semvers.add(l.name);
+    }
+  }
+  print(latestTag);
+  print(unreleasedPRs.first.toJson());
+  print(semvers);
+
+  var newVersion = '';
+  if (semvers.contains('semver:major')) {
+    newVersion = latestVersion.nextMajor.toString();
+  } else if (semvers.contains('semver:minor')) {
+    newVersion = latestVersion.nextMinor.toString();
+  } else if (semvers.contains('semver:patch')) {
+    newVersion = latestVersion.nextPatch.toString();
+  }
+  print(newVersion);
+  if (newVersion.isEmpty) {
+    return '';
+  }
+
+  var notes = await github.repositories.generateReleaseNotes(CreateReleaseNotes(
+      slug.owner, slug.name, newVersion,
+      previousTagName: latestTag));
+  return '${notes.name}\n${notes.body}';
+}
diff --git a/example/release_notes.html b/example/release_notes.html
new file mode 100644
index 00000000..9544974f
--- /dev/null
+++ b/example/release_notes.html
@@ -0,0 +1,21 @@
+
+
+
+
+  GitHub Release Notes
+
+
+
+  
+

GitHub Release Notes

+ +

+
+ +
+ + + + + + \ No newline at end of file diff --git a/example/releases.dart b/example/releases.dart index 8b8b528b..c244c962 100644 --- a/example/releases.dart +++ b/example/releases.dart @@ -1,35 +1,31 @@ +// ignore: deprecated_member_use import 'dart:html'; -import 'package:github/github.dart'; - import 'common.dart'; -DivElement releasesDiv; +DivElement? releasesDiv; Future main() async { await initViewSourceButton('releases.dart'); - releasesDiv = querySelector('#releases'); + releasesDiv = querySelector('#releases') as DivElement?; loadReleases(); } void loadReleases() { github.repositories - .listReleases(RepositorySlug('Workiva', 'wdesk')) + .listReleases(RepositorySlug('Workiva', 'w_common')) .take(10) .toList() .then((releases) { for (final release in releases) { - releasesDiv.appendHtml(''' + releasesDiv!.appendHtml('''

${release.name}

''', treeSanitizer: NodeTreeSanitizer.trusted); - final rel = releasesDiv.querySelector('#release-${release.id}'); + final rel = releasesDiv!.querySelector('#release-${release.id}'); void append(String key, String value) { - if (value == null) { - return; - } - rel.appendHtml('
$key: $value', + rel!.appendHtml('
$key: $value', treeSanitizer: NodeTreeSanitizer.trusted); } diff --git a/example/repos.dart b/example/repos.dart index 829eaee4..409417ab 100644 --- a/example/repos.dart +++ b/example/repos.dart @@ -1,58 +1,58 @@ import 'dart:async'; +// ignore: deprecated_member_use import 'dart:html'; -import 'package:github/github.dart'; - import 'common.dart'; -DivElement repositoriesDiv; -List repos; +DivElement? repositoriesDiv; +List? repos; Map> sorts = { 'stars': (Repository a, Repository b) => b.stargazersCount.compareTo(a.stargazersCount), 'forks': (Repository a, Repository b) => b.forksCount.compareTo(a.forksCount), - 'created': (Repository a, Repository b) => b.createdAt.compareTo(a.createdAt), - 'pushed': (Repository a, Repository b) => b.pushedAt.compareTo(a.pushedAt), + 'created': (Repository a, Repository b) => + b.createdAt!.compareTo(a.createdAt!), + 'pushed': (Repository a, Repository b) => b.pushedAt!.compareTo(a.pushedAt!), 'size': (Repository a, Repository b) => b.size.compareTo(a.size) }; Future main() async { await initViewSourceButton('repos.dart'); - repositoriesDiv = querySelector('#repos'); + repositoriesDiv = querySelector('#repos') as DivElement?; loadRepos(); - querySelector('#reload').onClick.listen((event) { + querySelector('#reload')!.onClick.listen((event) { loadRepos(); }); - sorts.keys.forEach((name) { - querySelector('#sort-$name').onClick.listen((event) { + for (final name in sorts.keys) { + querySelector('#sort-$name')!.onClick.listen((event) { if (_reposCache == null) { loadRepos(sorts[name]); } - updateRepos(_reposCache, sorts[name]); + updateRepos(_reposCache!, sorts[name]); }); - }); + } } -List _reposCache; +List? _reposCache; void updateRepos( List repos, [ - int Function(Repository a, Repository b) compare, + int Function(Repository a, Repository b)? compare, ]) { - document.querySelector('#repos').children.clear(); + document.querySelector('#repos')!.children.clear(); repos.sort(compare); for (final repo in repos) { - repositoriesDiv.appendHtml(''' + repositoriesDiv!.appendHtml('''

${repo.name}

- ${repo.description != "" && repo.description != null ? "Description: ${repo.description}
" : ""} - Language: ${repo.language ?? "Unknown"} + ${repo.description != "" ? "Description: ${repo.description}
" : ""} + Language: ${repo.language}
Default Branch: ${repo.defaultBranch}
@@ -69,15 +69,15 @@ void updateRepos( } } -void loadRepos([int Function(Repository a, Repository b) compare]) { - final title = querySelector('#title'); - if (title.text.contains('(')) { +void loadRepos([int Function(Repository a, Repository b)? compare]) { + final title = querySelector('#title')!; + if (title.text!.contains('(')) { title.replaceWith(HeadingElement.h2() ..text = 'GitHub for Dart - Repositories' ..id = 'title'); } - var user = 'SpinlockLabs'; + String? user = 'SpinlockLabs'; if (queryString.containsKey('user')) { user = queryString['user']; @@ -86,13 +86,13 @@ void loadRepos([int Function(Repository a, Repository b) compare]) { if (queryString.containsKey('sort') && compare == null) { final sorter = queryString['sort']; if (sorts.containsKey(sorter)) { - compare = sorts[sorter]; + compare = sorts[sorter!]; } } compare ??= (a, b) => a.name.compareTo(b.name); - github.repositories.listUserRepositories(user).toList().then((repos) { + github.repositories.listUserRepositories(user!).toList().then((repos) { _reposCache = repos; updateRepos(repos, compare); }); diff --git a/example/search.dart b/example/search.dart index 116581c5..aeee9cbb 100644 --- a/example/search.dart +++ b/example/search.dart @@ -1,16 +1,18 @@ +// ignore: deprecated_member_use import 'dart:html'; + import 'common.dart'; Future main() async { await initViewSourceButton('search.dart'); - final searchBtn = querySelector('#submit'); + final searchBtn = querySelector('#submit')!; searchBtn.onClick.listen(search); } Future search(_) async { final resultsStream = github.search.code( - val('query'), + val('query')!, language: val('language'), filename: val('filename'), user: val('user'), @@ -20,21 +22,21 @@ Future search(_) async { fork: val('fork'), path: val('path'), size: val('size'), - inFile: isChecked('infile'), - inPath: isChecked('inpath'), - perPage: int.tryParse(val('perpage')), - pages: int.tryParse(val('pages')), + inFile: isChecked('infile')!, + inPath: isChecked('inpath')!, + perPage: int.tryParse(val('perpage')!), + pages: int.tryParse(val('pages')!), ); - final DivElement resultsDiv = querySelector('#results'); + final resultsDiv = querySelector('#results') as DivElement; resultsDiv.innerHtml = ''; var count = 0; await for (final results in resultsStream) { - count += results.items.length; - querySelector('#nresults').text = + count += results.items!.length; + querySelector('#nresults')!.text = '${results.totalCount} result${results.totalCount == 1 ? "" : "s"} (showing $count)'; - for (final item in results.items) { + for (final item in results.items!) { final url = item.htmlUrl; final path = item.path; resultsDiv.append(DivElement() @@ -45,6 +47,6 @@ Future search(_) async { } } -String val(String id) => (querySelector('#$id') as InputElement).value; -bool isChecked(String id) => +String? val(String id) => (querySelector('#$id') as InputElement).value; +bool? isChecked(String id) => (querySelector('#$id') as CheckboxInputElement).checked; diff --git a/example/stars.dart b/example/stars.dart index c4b65f31..2bc50b4c 100644 --- a/example/stars.dart +++ b/example/stars.dart @@ -1,29 +1,21 @@ +// ignore: deprecated_member_use import 'dart:html'; -import 'package:github/github.dart'; import 'common.dart'; -DivElement $stars; +DivElement? $stars; Future main() async { await initViewSourceButton('stars.dart'); - $stars = querySelector('#stars'); + $stars = querySelector('#stars') as DivElement?; loadStars(); } void loadStars() { - var user = 'SpinlockLabs'; - var repo = 'github.dart'; + var user = queryString['user'] ?? 'SpinlockLabs'; + var repo = queryString['repo'] ?? 'github.dart'; - if (queryString.containsKey('user')) { - user = queryString['user']; - } - - if (queryString.containsKey('repo')) { - repo = queryString['repo']; - } - - querySelector('#title').appendText(' for $user/$repo'); + querySelector('#title')!.appendText(' for $user/$repo'); github.activity .listStargazers(RepositorySlug(user, repo)) @@ -36,9 +28,9 @@ void loadStars() { ..classes.add('avatar')); h.append(AnchorElement(href: stargazer.htmlUrl) ..append(ParagraphElement()..text = stargazer.login)); - $stars.append(h); + $stars!.append(h); }).onDone(() { - querySelector('#total') - .appendText(querySelectorAll('.user').length.toString() + ' stars'); + querySelector('#total')! + .appendText('${querySelectorAll('.user').length} stars'); }); } diff --git a/example/status.dart b/example/status.dart deleted file mode 100644 index a8036bd2..00000000 --- a/example/status.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:html'; - -Future main() async { - final request = await HttpRequest.request( - 'https://status.github.com/api/status.json', - requestHeaders: {'Origin': window.location.origin}, - ); - - final text = request.responseText; - final map = json.decode(text); - - querySelector('#status') - ..appendText(map['status']) - ..classes.add('status-${map["status"]}'); -} diff --git a/example/status.html b/example/status.html deleted file mode 100644 index f0156762..00000000 --- a/example/status.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - GitHub Status - - - - - - - - -
-

-
- - - - - - diff --git a/example/user_info.dart b/example/user_info.dart index c4360cf0..656207b1 100644 --- a/example/user_info.dart +++ b/example/user_info.dart @@ -1,26 +1,26 @@ +// ignore: deprecated_member_use import 'dart:html'; -import 'package:github/github.dart'; import 'common.dart'; -DivElement info; +DivElement? info; Future main() async { await initViewSourceButton('user_info.dart'); - info = document.getElementById('info'); + info = document.getElementById('info') as DivElement?; loadUser(); } -GitHub createClient(String token) { +GitHub createClient(String? token) { return GitHub(auth: Authentication.withToken(token)); } void loadUser() { - final localToken = document.getElementById('token') as InputElement; + final localToken = document.getElementById('token') as InputElement?; - final loadBtn = document.getElementById('load'); + final loadBtn = document.getElementById('load')!; loadBtn.onClick.listen((event) { - if (localToken.value == null || localToken.value.isEmpty) { + if (localToken!.value == null || localToken.value!.isEmpty) { window.alert('Please Enter a Token'); return; } @@ -28,15 +28,15 @@ void loadUser() { github = createClient(localToken.value); github.users.getCurrentUser().then((final CurrentUser user) { - info.children.clear(); - info.hidden = false; - info.appendHtml(''' + info!.children.clear(); + info!.hidden = false; + info!.appendHtml(''' Name: ${user.name} '''); void append(String name, dynamic value) { if (value != null) { - info.appendHtml(''' + info!.appendHtml('''
$name: ${value.toString()} '''); @@ -49,10 +49,10 @@ void loadUser() { append('Followers', user.followersCount); append('Following', user.followingCount); append('Disk Usage', user.diskUsage); - append('Plan Name', user.plan.name); + append('Plan Name', user.plan!.name); append('Created', user.createdAt); - document.getElementById('load').hidden = true; - document.getElementById('token').hidden = true; + document.getElementById('load')!.hidden = true; + document.getElementById('token')!.hidden = true; }).catchError((e) { if (e is AccessForbidden) { window.alert('Invalid Token'); @@ -61,7 +61,7 @@ void loadUser() { }); if (github.auth.token != null) { - localToken.value = github.auth.token; + localToken!.value = github.auth.token; loadBtn.click(); } } diff --git a/example/users.dart b/example/users.dart index 40e22077..003d3f5d 100644 --- a/example/users.dart +++ b/example/users.dart @@ -1,15 +1,14 @@ import 'dart:async'; +// ignore: deprecated_member_use import 'dart:html'; -import 'package:github/github.dart'; - import 'common.dart'; -DivElement usersDiv; +DivElement? usersDiv; Future main() async { await initViewSourceButton('users.dart'); - usersDiv = querySelector('#users'); + usersDiv = querySelector('#users') as DivElement?; loadUsers(); } @@ -31,7 +30,7 @@ void loadUsers() { ..writeln('Created: ${user.createdAt}') ..writeln('Updated: ${user.updatedAt}'); - if (user.company != null && user.company.isNotEmpty) { + if (user.company != null && user.company!.isNotEmpty) { buff.writeln('Company: ${user.company}'); } @@ -41,7 +40,7 @@ void loadUsers() { ..appendHtml(buff.toString().replaceAll('\n', '
'), treeSanitizer: NodeTreeSanitizer.trusted)); - usersDiv.append(userDiv); + usersDiv!.append(userDiv); }); }); } diff --git a/example/view_source.html b/example/view_source.html deleted file mode 100755 index 38b4b3af..00000000 --- a/example/view_source.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - View Source - - - - - - - -
- - - - - - diff --git a/example/view_source.js b/example/view_source.js deleted file mode 100755 index 01ca35f1..00000000 --- a/example/view_source.js +++ /dev/null @@ -1,81 +0,0 @@ -var args = document.location.search.substring(1).split('&'); - -var opts = {}; - -for (var i = 0; i < args.length; i++) { - var arg = window.decodeURIComponent(args[i]); - - if (arg.indexOf('=') == -1) { - opts[arg.trim()] = true; - } else { - var kvp = arg.split('='); - opts[kvp[0].trim()] = kvp[1].trim(); - } -} - -function opt(name, def) { - if (Object.keys(opts).indexOf(name) !== -1) { - return opts[name]; - } else { - return def; - } -} - -function createEditor(code) { - var editor = ace.edit("editor"); - - editor.focus(); - editor.setReadOnly(opts['editable'] ? true : false); - - editor.commands.addCommand({ - name: 'saveFile', - bindKey: { - win: 'Ctrl-S', - mac: 'Command-S', - sender: 'editor|cli' - }, - exec: function() {} - }); - - editor.setTheme("ace/theme/" + opt("theme", "github")); - editor.getSession().setMode("ace/mode/" + opt("mode", "dart")); - editor.setShowPrintMargin(false); - editor.setValue(code, 0); - editor.clearSelection(); - editor.moveCursorTo(0, 0); - editor.setReadOnly(true); -} - -function receiveMessage(event) { - var msg = event.data; - - if (msg.command === "code") { - createEditor(msg.code); - } -} - -if (window.opener !== null) { - window.addEventListener("message", receiveMessage); -} else { - if (Object.keys(opts).indexOf("path") !== -1) { - var req = new XMLHttpRequest(); - req.open("GET", opts.path); - - req.onreadystatechange = function() { - if (req.readyState === XMLHttpRequest.DONE) { - if (req.status === 200) { - createEditor(req.responseText); - } else { - createEditor("ERROR: " + opts.path + " was not found."); - } - } - }; - req.send(); - } -} - -ready(function () { - if (window.opener) { - window.opener.postMessage({ "command": "ready" }, "*"); - } -}); \ No newline at end of file diff --git a/example/zen.dart b/example/zen.dart index b152e262..34c55c87 100644 --- a/example/zen.dart +++ b/example/zen.dart @@ -1,8 +1,10 @@ +// ignore: deprecated_member_use import 'dart:html'; + import 'common.dart'; Future main() async { await initViewSourceButton('zen.dart'); final msg = await github.misc.getZen(); - querySelector('#zen').text = msg; + querySelector('#zen')!.text = msg; } diff --git a/test/git_integration_test.dart b/integration_test/git_integration_test.dart similarity index 84% rename from test/git_integration_test.dart rename to integration_test/git_integration_test.dart index 1d95c676..3ff4d113 100644 --- a/test/git_integration_test.dart +++ b/integration_test/git_integration_test.dart @@ -5,20 +5,22 @@ import 'package:github/github.dart'; import 'package:test/test.dart'; void main() { - String firstCommitSha; - String firstCommitTreeSha; + String? firstCommitSha; + String? firstCommitTreeSha; - String createdTreeSha; - String createdCommitSha; + String? createdTreeSha; + String? createdCommitSha; - GitHub github; - RepositorySlug slug; + late GitHub github; + late RepositorySlug slug; setUpAll(() { final authToken = Platform.environment['GITHUB_API_TOKEN']; final repoOwner = Platform.environment['GITHUB_DART_TEST_REPO_OWNER']; final repoName = Platform.environment['GITHUB_DART_TEST_REPO_NAME']; - + if (repoName == null || repoOwner == null) { + throw AssertionError('config incorrect'); + } github = GitHub(auth: Authentication.withToken(authToken)); slug = RepositorySlug(repoOwner, repoName); }); @@ -30,8 +32,8 @@ void main() { // Test definitions. test('get last commit of master', () async { final branch = await github.repositories.getBranch(slug, 'master'); - firstCommitSha = branch.commit.sha; - firstCommitTreeSha = branch.commit.commit.sha; + firstCommitSha = branch.commit!.sha; + firstCommitTreeSha = branch.commit!.commit!.sha; }); test('create and get a new blob', () async { @@ -43,7 +45,7 @@ void main() { final fetchedBlob = await github.git.getBlob(slug, createdBlobSha); - final base64Decoded = base64Decode(fetchedBlob.content); + final base64Decoded = base64Decode(fetchedBlob.content!); expect(utf8.decode(base64Decoded), equals('bbb')); expect(fetchedBlob.encoding, equals('base64')); @@ -72,7 +74,7 @@ void main() { final fetchedTree = await github.git.getTree(slug, createdTreeSha); expect(fetchedTree.sha, equals(createdTreeSha)); - expect(fetchedTree.entries.length, equals(2)); + expect(fetchedTree.entries!.length, equals(2)); }); test('create and get a new commit', () async { @@ -87,8 +89,8 @@ void main() { final fetchedCommit = await github.git.getCommit(slug, createdCommitSha); expect(fetchedCommit.sha, equals(createdCommitSha)); expect(fetchedCommit.message, equals('My test commit')); - expect(fetchedCommit.tree.sha, equals(createdTreeSha)); - expect(fetchedCommit.parents.first.sha, equals(firstCommitSha)); + expect(fetchedCommit.tree!.sha, equals(createdTreeSha)); + expect(fetchedCommit.parents!.first.sha, equals(firstCommitSha)); }); test('update heads/master reference to new commit', () { @@ -103,8 +105,8 @@ void main() { final fetchedRef = await github.git.getReference(slug, 'heads/$branchName'); expect(fetchedRef.ref, equals('refs/heads/$branchName')); - expect(fetchedRef.object.type, equals('commit')); - expect(fetchedRef.object.sha, equals(createdCommitSha)); + expect(fetchedRef.object!.type, equals('commit')); + expect(fetchedRef.object!.sha, equals(createdCommitSha)); }); test('create and get a new tag', () async { @@ -122,8 +124,8 @@ void main() { expect(fetchedTag.tag, equals(tagName)); expect(fetchedTag.sha, equals(createdTagSha)); expect(fetchedTag.message, equals('Version 0.0.1')); - expect(fetchedTag.tagger.name, equals('aName')); - expect(fetchedTag.object.sha, equals(createdCommitSha)); + expect(fetchedTag.tagger!.name, equals('aName')); + expect(fetchedTag.object!.sha, equals(createdCommitSha)); // Create a reference for the tag. await github.git.createReference(slug, 'refs/tags/$tagName', createdTagSha); diff --git a/lib/browser_helper.dart b/lib/browser_helper.dart index 4c8485e3..39ebd393 100644 --- a/lib/browser_helper.dart +++ b/lib/browser_helper.dart @@ -1,4 +1,6 @@ +// ignore: deprecated_member_use import 'dart:html'; + import 'package:github/src/common.dart'; /// Renders Markdown in HTML using the GitHub API @@ -14,7 +16,7 @@ void renderMarkdown(GitHub github, String selector, {int indent = 4}) { elements.removeWhere((Element it) => it.attributes.containsKey('rendered')); for (final e in elements) { - final txt = e.text; + final txt = e.text!; final md = txt.split('\n').map((it) { return it.length >= indent ? it.substring(indent) : it; diff --git a/lib/github.dart b/lib/github.dart index 03396535..6b79e1c4 100644 --- a/lib/github.dart +++ b/lib/github.dart @@ -1,7 +1,7 @@ +export 'package:github/src/common.dart'; + /// Do a conditional export of the right cross platform pieces depending on /// if dart.html or dart.io is available. export 'package:github/src/common/xplat_common.dart' if (dart.library.html) 'package:github/src/browser/xplat_browser.dart' if (dart.library.io) 'package:github/src/server/xplat_server.dart'; - -export 'package:github/src/common.dart'; diff --git a/lib/hooks.dart b/lib/hooks.dart index 1a336ed2..19fc3fae 100644 --- a/lib/hooks.dart +++ b/lib/hooks.dart @@ -7,7 +7,6 @@ /// Add this import if you are in a non-web environment and writing something /// that uses github hooks. For more information, see github hooks documentation /// https://developer.github.com/v3/repos/hooks/ - -library hooks; +library; export 'src/server/xplat_server.dart'; diff --git a/lib/src/browser/xplat_browser.dart b/lib/src/browser/xplat_browser.dart index 79aeeb17..c54a2530 100644 --- a/lib/src/browser/xplat_browser.dart +++ b/lib/src/browser/xplat_browser.dart @@ -1,4 +1,6 @@ +// ignore: deprecated_member_use import 'dart:html'; + import 'package:github/src/common.dart'; import 'package:github/src/common/xplat_common.dart' show findAuthenticationInMap; @@ -11,7 +13,7 @@ Authentication findAuthenticationFromEnvironment() { // search the query string parameters first var auth = findAuthenticationInMap(_parseQuery(window.location.href)); auth ??= findAuthenticationInMap(window.sessionStorage); - return auth ?? Authentication.anonymous(); + return auth ?? const Authentication.anonymous(); } /// Parse the query string to a parameter `Map` diff --git a/lib/src/common.dart b/lib/src/common.dart index f0313746..df783a26 100644 --- a/lib/src/common.dart +++ b/lib/src/common.dart @@ -1,5 +1,7 @@ /// The Core of GitHub for Dart. /// Contains the Models and other GitHub stuff. +library; + export 'package:github/src/common/activity_service.dart'; export 'package:github/src/common/authorizations_service.dart'; export 'package:github/src/common/checks_service.dart'; @@ -31,6 +33,8 @@ export 'package:github/src/common/model/repos_releases.dart'; export 'package:github/src/common/model/repos_stats.dart'; export 'package:github/src/common/model/repos_statuses.dart'; export 'package:github/src/common/model/search.dart'; +export 'package:github/src/common/model/timeline.dart'; +export 'package:github/src/common/model/timeline_support.dart'; export 'package:github/src/common/model/users.dart'; export 'package:github/src/common/orgs_service.dart'; export 'package:github/src/common/pulls_service.dart'; @@ -46,4 +50,5 @@ export 'package:github/src/common/util/oauth2.dart'; export 'package:github/src/common/util/pagination.dart'; export 'package:github/src/common/util/service.dart'; export 'package:github/src/common/util/utils.dart'; +export 'package:github/src/const/language_color.dart'; export 'package:github/src/const/token_env_keys.dart'; diff --git a/lib/src/common/activity_service.dart b/lib/src/common/activity_service.dart index 6fc03bb7..f97aefcf 100644 --- a/lib/src/common/activity_service.dart +++ b/lib/src/common/activity_service.dart @@ -1,9 +1,7 @@ import 'dart:async'; import 'dart:convert'; + import 'package:github/src/common.dart'; -import 'package:github/src/common/model/users.dart'; -import 'package:github/src/common/util/pagination.dart'; -import 'package:github/src/common/util/utils.dart'; import 'package:http/http.dart' as http; /// The [ActivityService] handles communication with activity related methods @@ -11,14 +9,14 @@ import 'package:http/http.dart' as http; /// /// API docs: https://developer.github.com/v3/activity/ class ActivityService extends Service { - ActivityService(GitHub github) : super(github); + ActivityService(super.github); /// Lists public events. /// /// API docs: https://developer.github.com/v3/activity/events/#list-public-events Stream listPublicEvents({int pages = 2}) { return PaginationHelper(github) - .objects('GET', '/events', (i) => Event.fromJson(i), pages: pages); + .objects('GET', '/events', Event.fromJson, pages: pages); } /// Lists public events for a network of repositories. @@ -27,7 +25,7 @@ class ActivityService extends Service { Stream listRepositoryNetworkEvents(RepositorySlug slug, {int pages = 2}) { return PaginationHelper(github).objects( - 'GET', '/networks/${slug.fullName}/events', (i) => Event.fromJson(i), + 'GET', '/networks/${slug.fullName}/events', Event.fromJson, pages: pages); } @@ -46,9 +44,9 @@ class ActivityService extends Service { /// Lists repository issue events. /// /// API docs: https://developer.github.com/v3/activity/events/#list-repository-events - Stream listRepositoryIssueEvents(RepositorySlug slug, {int pages}) { - return PaginationHelper(github).objects('GET', - '/repos/${slug.fullName}/issues/events', (i) => Event.fromJson(i), + Stream listRepositoryIssueEvents(RepositorySlug slug, {int? pages}) { + return PaginationHelper(github).objects( + 'GET', '/repos/${slug.fullName}/issues/events', Event.fromJson, pages: pages); } @@ -60,9 +58,9 @@ class ActivityService extends Service { /// Lists repository events. /// /// API docs: https://developer.github.com/v3/activity/events/#list-repository-events - Stream listRepositoryEvents(RepositorySlug slug, {int pages}) { + Stream listRepositoryEvents(RepositorySlug slug, {int? pages}) { return PaginationHelper(github).objects( - 'GET', '/repos/${slug.fullName}/events', (i) => Event.fromJson(i), + 'GET', '/repos/${slug.fullName}/events', Event.fromJson, pages: pages); } @@ -75,10 +73,9 @@ class ActivityService extends Service { /// Lists public events for an organization. /// /// API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-an-organization - Stream listEventsForOrganization(String name, {int pages}) { - return PaginationHelper(github).objects( - 'GET', '/orgs/$name/events', (i) => Event.fromJson(i), - pages: pages); + Stream listEventsForOrganization(String name, {int? pages}) { + return PaginationHelper(github) + .objects('GET', '/orgs/$name/events', Event.fromJson, pages: pages); } /// Returns an [EventPoller] for public events for an organization. @@ -102,18 +99,18 @@ class ActivityService extends Service { /// Lists the events performed by a user. /// /// API docs: https://developer.github.com/v3/activity/events/#list-events-performed-by-a-user - Stream listEventsPerformedByUser(String username, {int pages}) { + Stream listEventsPerformedByUser(String username, {int? pages}) { return PaginationHelper(github).objects( - 'GET', '/users/$username/events', (i) => Event.fromJson(i), + 'GET', '/users/$username/events', Event.fromJson, pages: pages); } /// Lists the public events performed by a user. /// /// API docs: https://developer.github.com/v3/activity/events/#list-public-events-performed-by-a-user - Stream listPublicEventsPerformedByUser(String username, {int pages}) { + Stream listPublicEventsPerformedByUser(String username, {int? pages}) { return PaginationHelper(github).objects( - 'GET', '/users/$username/events/public', (i) => Event.fromJson(i), + 'GET', '/users/$username/events/public', Event.fromJson, pages: pages); } @@ -131,7 +128,7 @@ class ActivityService extends Service { Stream listNotifications( {bool all = false, bool participating = false}) { return PaginationHelper(github).objects( - 'GET', '/notifications', (i) => Notification.fromJson(i), + 'GET', '/notifications', Notification.fromJson, params: {'all': all, 'participating': participating}); } @@ -140,17 +137,15 @@ class ActivityService extends Service { /// API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository Stream listRepositoryNotifications(RepositorySlug repository, {bool all = false, bool participating = false}) { - return PaginationHelper(github).objects( - 'GET', - '/repos/${repository.fullName}/notifications', - (i) => Notification.fromJson(i), + return PaginationHelper(github).objects('GET', + '/repos/${repository.fullName}/notifications', Notification.fromJson, params: {'all': all, 'participating': participating}); } /// Marks all notifications up to [lastRead] as read. /// /// API docs: https://developer.github.com/v3/activity/notifications/#mark-as-read - Future markNotificationsRead({DateTime lastRead}) { + Future markNotificationsRead({DateTime? lastRead}) { final data = {}; if (lastRead != null) { @@ -170,7 +165,7 @@ class ActivityService extends Service { /// API docs:https://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository Future markRepositoryNotificationsRead( RepositorySlug slug, { - DateTime lastRead, + DateTime? lastRead, }) { final data = {}; @@ -191,7 +186,7 @@ class ActivityService extends Service { /// API docs: https://developer.github.com/v3/activity/notifications/#view-a-single-thread Future getThread(String threadId) => github.getJSON('/notification/threads/$threadId', - statusCode: StatusCodes.OK, convert: (i) => Notification.fromJson(i)); + statusCode: StatusCodes.OK, convert: Notification.fromJson); /// Mark the specified notification thread as read. /// @@ -211,25 +206,28 @@ class ActivityService extends Service { /// Lists people who have starred the specified repo. /// /// API docs: https://developer.github.com/v3/activity/starring/#list-stargazers - Stream listStargazers(RepositorySlug slug) { + Stream listStargazers(RepositorySlug slug, {int perPage = 30}) { return PaginationHelper(github).objects( - 'GET', '/repos/${slug.fullName}/stargazers', (i) => User.fromJson(i)); + 'GET', '/repos/${slug.fullName}/stargazers', User.fromJson, + params: {'per_page': perPage}); } /// Lists all the repos starred by a user. /// /// API docs: https://developer.github.com/v3/activity/starring/#list-repositories-being-starred - Stream listStarredByUser(String user) { - return PaginationHelper(github) - .objects('GET', '/users/$user/starred', (i) => Repository.fromJson(i)); + Stream listStarredByUser(String user, {int perPage = 30}) { + return PaginationHelper(github).objects( + 'GET', '/users/$user/starred', Repository.fromJson, + params: {'per_page': perPage}); } /// Lists all the repos by the current user. /// /// API docs: https://developer.github.com/v3/activity/starring/#list-repositories-being-starred - Stream listStarred() { - return PaginationHelper(github) - .objects('GET', '/user/starred', (i) => Repository.fromJson(i)); + Stream listStarred({int perPage = 30}) { + return PaginationHelper(github).objects( + 'GET', '/user/starred', Repository.fromJson, + params: {'per_page': perPage}); } /// Checks if the currently authenticated user has starred the specified repository. @@ -267,16 +265,16 @@ class ActivityService extends Service { /// /// API docs: https://developer.github.com/v3/activity/watching/#list-watchers Stream listWatchers(RepositorySlug slug) { - return PaginationHelper(github).objects( - 'GET', '/repos/${slug.fullName}/subscribers', (i) => User.fromJson(i)); + return PaginationHelper(github) + .objects('GET', '/repos/${slug.fullName}/subscribers', User.fromJson); } /// Lists the repositories the specified user is watching. /// /// API docs: https://developer.github.com/v3/activity/watching/#list-repositories-being-watched Stream listWatchedByUser(String user) { - return PaginationHelper(github).objects( - 'GET', '/users/$user/subscriptions', (i) => Repository.fromJson(i)); + return PaginationHelper(github) + .objects('GET', '/users/$user/subscriptions', Repository.fromJson); } /// Lists the repositories the current user is watching. @@ -284,7 +282,7 @@ class ActivityService extends Service { /// API docs: https://developer.github.com/v3/activity/watching/#list-repositories-being-watched Stream listWatched() { return PaginationHelper(github) - .objects('GET', '/user/subscriptions', (i) => Repository.fromJson(i)); + .objects('GET', '/user/subscriptions', Repository.fromJson); } /// Fetches repository subscription information. @@ -293,24 +291,23 @@ class ActivityService extends Service { Future getRepositorySubscription( RepositorySlug slug) => github.getJSON('/repos/${slug.fullName}/subscription', - statusCode: StatusCodes.OK, - convert: (i) => RepositorySubscription.fromJson(i)); + statusCode: StatusCodes.OK, convert: RepositorySubscription.fromJson); /// Sets the Repository Subscription Status /// /// API docs: https://developer.github.com/v3/activity/watching/#set-a-repository-subscription Future setRepositorySubscription( RepositorySlug slug, { - bool subscribed, - bool ignored, + bool? subscribed, + bool? ignored, }) { final map = - createNonNullMap({'subscribed': subscribed, 'ignored': ignored}); + createNonNullMap({'subscribed': subscribed!, 'ignored': ignored!}); return github.putJSON( '/repos/${slug.fullName}/subscription', statusCode: StatusCodes.OK, - convert: (i) => RepositorySubscription.fromJson(i), + convert: RepositorySubscription.fromJson, body: GitHubJson.encode(map), ); } @@ -329,16 +326,16 @@ class ActivityService extends Service { class EventPoller { final GitHub github; final String path; - final List handledEvents = []; + final List handledEvents = []; - Timer _timer; - StreamController _controller; + Timer? _timer; + StreamController? _controller; - String _lastFetched; + String? _lastFetched; EventPoller(this.github, this.path); - Stream start({bool onlyNew = false, int interval, DateTime after}) { + Stream start({bool onlyNew = false, int? interval, DateTime? after}) { if (_timer != null) { throw Exception('Polling already started.'); } @@ -350,7 +347,7 @@ class EventPoller { _controller = StreamController(); void handleEvent(http.Response response) { - interval ??= int.parse(response.headers['x-poll-interval']); + interval ??= int.parse(response.headers['x-poll-interval']!); if (response.statusCode == 304) { return; @@ -364,7 +361,9 @@ class EventPoller { for (final item in json) { final event = Event.fromJson(item); - if (after == null ? false : event.createdAt.toUtc().isBefore(after)) { + if (after == null + ? false + : event.createdAt!.toUtc().isBefore(after)) { continue; } @@ -374,15 +373,15 @@ class EventPoller { handledEvents.add(event.id); - _controller.add(event); + _controller!.add(event); } } - _timer ??= Timer.periodic(Duration(seconds: interval), (timer) { + _timer ??= Timer.periodic(Duration(seconds: interval!), (timer) { final headers = {}; if (_lastFetched != null) { - headers['If-None-Match'] = _lastFetched; + headers['If-None-Match'] = _lastFetched ?? ''; } github.request('GET', path, headers: headers).then(handleEvent); @@ -392,12 +391,12 @@ class EventPoller { final headers = {}; if (_lastFetched != null) { - headers['If-None-Match'] = _lastFetched; + headers['If-None-Match'] = _lastFetched ?? ''; } github.request('GET', path, headers: headers).then(handleEvent); - return _controller.stream; + return _controller!.stream; } Future stop() { @@ -405,8 +404,8 @@ class EventPoller { throw Exception('Polling not started.'); } - _timer.cancel(); - final future = _controller.close(); + _timer!.cancel(); + final future = _controller!.close(); _timer = null; _controller = null; diff --git a/lib/src/common/authorizations_service.dart b/lib/src/common/authorizations_service.dart index 7a1e44f1..68b80c25 100644 --- a/lib/src/common/authorizations_service.dart +++ b/lib/src/common/authorizations_service.dart @@ -1,6 +1,6 @@ import 'dart:async'; + import 'package:github/src/common.dart'; -import 'package:github/src/common/util/pagination.dart'; /// The [AuthorizationsService] handles communication with authorizations related methods /// of the GitHub API. @@ -10,14 +10,14 @@ import 'package:github/src/common/util/pagination.dart'; /// /// API docs: https://developer.github.com/v3/oauth_authorizations/ class AuthorizationsService extends Service { - AuthorizationsService(GitHub github) : super(github); + AuthorizationsService(super.github); /// Lists all authorizations. /// /// API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-authorizations Stream listAuthorizations() { return PaginationHelper(github) - .objects('GET', '/authorizations', (i) => Authorization.fromJson(i)); + .objects('GET', '/authorizations', Authorization.fromJson); } /// Fetches an authorization specified by [id]. @@ -25,7 +25,7 @@ class AuthorizationsService extends Service { /// API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-authorization Future getAuthorization(int id) => github.getJSON('/authorizations/$id', - statusCode: 200, convert: (i) => Authorization.fromJson(i)); + statusCode: 200, convert: Authorization.fromJson); // TODO: Implement remaining API methods of authorizations: // See https://developer.github.com/v3/oauth_authorizations/ diff --git a/lib/src/common/checks_service.dart b/lib/src/common/checks_service.dart index f25ee2c6..f3085197 100644 --- a/lib/src/common/checks_service.dart +++ b/lib/src/common/checks_service.dart @@ -1,8 +1,6 @@ import 'dart:convert'; import 'package:github/github.dart'; -import 'package:github/src/common/util/utils.dart'; -import 'package:meta/meta.dart'; const _previewHeader = 'application/vnd.github.antiope-preview+json'; @@ -13,21 +11,20 @@ class ChecksService extends Service { /// Methods to interact with Check Runs. /// /// API docs: https://developer.github.com/v3/checks/runs/ - final _CheckRunsService checkRuns; + final CheckRunsService checkRuns; /// Methods to interact with Check Suites. /// /// API docs: https://developer.github.com/v3/checks/suites/ - final _CheckSuitesService checkSuites; + final CheckSuitesService checkSuites; - ChecksService(GitHub github) - : checkRuns = _CheckRunsService._(github), - checkSuites = _CheckSuitesService._(github), - super(github); + ChecksService(super.github) + : checkRuns = CheckRunsService._(github), + checkSuites = CheckSuitesService._(github); } -class _CheckRunsService extends Service { - _CheckRunsService._(GitHub github) : super(github); +class CheckRunsService extends Service { + CheckRunsService._(super.github); /// Creates a new check run for a specific commit in a repository. /// Your GitHub App must have the `checks:write` permission to create check runs. @@ -50,16 +47,16 @@ class _CheckRunsService extends Service { /// API docs: https://developer.github.com/v3/checks/runs/#create-a-check-run Future createCheckRun( RepositorySlug slug, { - @required String name, - @required String headSha, - String detailsUrl, - String externalId, + required String name, + required String headSha, + String? detailsUrl, + String? externalId, CheckRunStatus status = CheckRunStatus.queued, - DateTime startedAt, - CheckRunConclusion conclusion, - DateTime completedAt, - CheckRunOutput output, - List actions, + DateTime? startedAt, + CheckRunConclusion? conclusion, + DateTime? completedAt, + CheckRunOutput? output, + List? actions, }) async { assert(conclusion != null || (completedAt == null && status != CheckRunStatus.completed)); @@ -80,7 +77,7 @@ class _CheckRunsService extends Service { 'output': output, 'actions': actions, })), - convert: (i) => CheckRun.fromJson(i), + convert: CheckRun.fromJson, ); } @@ -102,15 +99,15 @@ class _CheckRunsService extends Service { Future updateCheckRun( RepositorySlug slug, CheckRun checkRunToUpdate, { - String name, - String detailsUrl, - String externalId, - DateTime startedAt, + String? name, + String? detailsUrl, + String? externalId, + DateTime? startedAt, CheckRunStatus status = CheckRunStatus.queued, - CheckRunConclusion conclusion, - DateTime completedAt, - CheckRunOutput output, - List actions, + CheckRunConclusion? conclusion, + DateTime? completedAt, + CheckRunOutput? output, + List? actions, }) async { assert(conclusion != null || (completedAt == null && status != CheckRunStatus.completed)); @@ -131,7 +128,7 @@ class _CheckRunsService extends Service { 'output': output, 'actions': actions, })), - convert: (i) => CheckRun.fromJson(i), + convert: CheckRun.fromJson, ); } @@ -146,16 +143,16 @@ class _CheckRunsService extends Service { /// API docs: https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-specific-ref Stream listCheckRunsForRef( RepositorySlug slug, { - @required String ref, - String checkName, - CheckRunStatus status, - CheckRunFilter filter, + required String ref, + String? checkName, + CheckRunStatus? status, + CheckRunFilter? filter, }) { ArgumentError.checkNotNull(ref); return PaginationHelper(github).objects, CheckRun>( 'GET', 'repos/$slug/commits/$ref/check-runs', - (input) => CheckRun.fromJson(input), + CheckRun.fromJson, statusCode: StatusCodes.OK, preview: _previewHeader, params: createNonNullMap({ @@ -177,16 +174,16 @@ class _CheckRunsService extends Service { /// API docs: https://developer.github.com/v3/checks/runs/#list-check-runs-in-a-check-suite Stream listCheckRunsInSuite( RepositorySlug slug, { - @required int checkSuiteId, - String checkName, - CheckRunStatus status, - CheckRunFilter filter, + required int checkSuiteId, + String? checkName, + CheckRunStatus? status, + CheckRunFilter? filter, }) { ArgumentError.checkNotNull(checkSuiteId); return PaginationHelper(github).objects, CheckRun>( 'GET', 'repos/$slug/check-suites/$checkSuiteId/check-runs', - (input) => CheckRun.fromJson(input), + CheckRun.fromJson, statusCode: StatusCodes.OK, preview: _previewHeader, params: createNonNullMap({ @@ -205,14 +202,14 @@ class _CheckRunsService extends Service { /// API docs: https://developer.github.com/v3/checks/runs/#get-a-single-check-run Future getCheckRun( RepositorySlug slug, { - @required int checkRunId, + required int checkRunId, }) { ArgumentError.checkNotNull(checkRunId); return github.getJSON, CheckRun>( 'repos/${slug.fullName}/check-runs/$checkRunId', preview: _previewHeader, statusCode: StatusCodes.OK, - convert: (i) => CheckRun.fromJson(i), + convert: CheckRun.fromJson, ); } @@ -223,21 +220,21 @@ class _CheckRunsService extends Service { /// API docs: https://developer.github.com/v3/checks/runs/#list-annotations-for-a-check-run Stream listAnnotationsInCheckRun( RepositorySlug slug, { - @required CheckRun checkRun, + required CheckRun checkRun, }) { return PaginationHelper(github) .objects, CheckRunAnnotation>( 'GET', '/repos/${slug.fullName}/check-runs/${checkRun.id}/annotations', - (i) => CheckRunAnnotation.fromJSON(i), + CheckRunAnnotation.fromJSON, statusCode: StatusCodes.OK, preview: _previewHeader, ); } } -class _CheckSuitesService extends Service { - _CheckSuitesService._(GitHub github) : super(github); +class CheckSuitesService extends Service { + CheckSuitesService._(super.github); /// Gets a single check suite using its `id`. /// GitHub Apps must have the `checks:read` permission on a private repository or pull access to a public repository to get check suites. @@ -246,13 +243,13 @@ class _CheckSuitesService extends Service { /// API docs: https://developer.github.com/v3/checks/suites/#get-a-single-check-suite Future getCheckSuite( RepositorySlug slug, { - @required int checkSuiteId, + required int checkSuiteId, }) async { ArgumentError.checkNotNull(checkSuiteId); return github.requestJson( 'GET', 'repos/$slug/check-suites/$checkSuiteId', - convert: (input) => CheckSuite.fromJson(input), + convert: CheckSuite.fromJson, preview: _previewHeader, statusCode: StatusCodes.OK, ); @@ -268,15 +265,15 @@ class _CheckSuitesService extends Service { /// API docs: https://developer.github.com/v3/checks/suites/#list-check-suites-for-a-specific-ref Stream listCheckSuitesForRef( RepositorySlug slug, { - @required String ref, - int appId, - String checkName, + required String ref, + int? appId, + String? checkName, }) { ArgumentError.checkNotNull(ref); return PaginationHelper(github).objects, CheckSuite>( 'GET', 'repos/$slug/commits/$ref/check-suites', - (input) => CheckSuite.fromJson(input), + CheckSuite.fromJson, preview: _previewHeader, params: createNonNullMap({ 'app_id': appId, @@ -296,7 +293,7 @@ class _CheckSuitesService extends Service { /// API docs: https://developer.github.com/v3/checks/suites/#update-repository-preferences-for-check-suites Future> updatePreferencesForCheckSuites( RepositorySlug slug, { - @required List autoTriggerChecks, + required List autoTriggerChecks, }) { ArgumentError.checkNotNull(autoTriggerChecks); return github.requestJson, List>( @@ -306,7 +303,8 @@ class _CheckSuitesService extends Service { preview: _previewHeader, body: {'auto_trigger_checks': autoTriggerChecks}, convert: (input) => (input['preferences']['auto_trigger_checks'] as List) - .map((e) => AutoTriggerChecks.fromJson(e)) + .cast>() + .map(AutoTriggerChecks.fromJson) .toList(), ); } @@ -319,7 +317,7 @@ class _CheckSuitesService extends Service { /// API docs: https://developer.github.com/v3/checks/suites/#create-a-check-suite Future createCheckSuite( RepositorySlug slug, { - @required String headSha, + required String headSha, }) { ArgumentError.checkNotNull(headSha); return github.requestJson, CheckSuite>( @@ -328,7 +326,7 @@ class _CheckSuitesService extends Service { statusCode: StatusCodes.CREATED, preview: _previewHeader, params: {'head_sha': headSha}, - convert: (input) => CheckSuite.fromJson(input), + convert: CheckSuite.fromJson, ); } @@ -340,7 +338,7 @@ class _CheckSuitesService extends Service { /// API docs: https://developer.github.com/v3/checks/suites/#rerequest-check-suite Future reRequestCheckSuite( RepositorySlug slug, { - @required int checkSuiteId, + required int checkSuiteId, }) { ArgumentError.checkNotNull(checkSuiteId); return github.request( diff --git a/lib/src/common/gists_service.dart b/lib/src/common/gists_service.dart index 89b266bf..05ac62bd 100644 --- a/lib/src/common/gists_service.dart +++ b/lib/src/common/gists_service.dart @@ -1,21 +1,21 @@ import 'dart:async'; import 'dart:convert'; + import 'package:github/src/common.dart'; -import 'package:github/src/common/util/pagination.dart'; /// The [GistsService] handles communication with gist /// methods of the GitHub API. /// /// API docs: https://developer.github.com/v3/gists/ class GistsService extends Service { - GistsService(GitHub github) : super(github); + GistsService(super.github); /// lists gists for a user. /// /// API docs: https://developer.github.com/v3/gists/#list-gists Stream listUserGists(String username) { return PaginationHelper(github) - .objects('GET', '/users/$username/gists', (i) => Gist.fromJson(i)); + .objects('GET', '/users/$username/gists', Gist.fromJson); } /// Fetches the gists for the currently authenticated user. @@ -23,8 +23,7 @@ class GistsService extends Service { /// /// API docs: https://developer.github.com/v3/gists/#list-gists Stream listCurrentUserGists() { - return PaginationHelper(github) - .objects('GET', '/gists', (i) => Gist.fromJson(i)); + return PaginationHelper(github).objects('GET', '/gists', Gist.fromJson); } /// Fetches the currently authenticated user's public gists. @@ -32,7 +31,7 @@ class GistsService extends Service { /// API docs: https://developer.github.com/v3/gists/#list-gists Stream listCurrentUserPublicGists() { return PaginationHelper(github) - .objects('GET', '/gists/public', (i) => Gist.fromJson(i)); + .objects('GET', '/gists/public', Gist.fromJson); } /// Fetches the currently authenticated user's starred gists. @@ -40,21 +39,21 @@ class GistsService extends Service { /// API docs: https://developer.github.com/v3/gists/#list-gists Stream listCurrentUserStarredGists() { return PaginationHelper(github) - .objects('GET', '/gists/starred', (i) => Gist.fromJson(i)); + .objects('GET', '/gists/starred', Gist.fromJson); } /// Fetches a Gist by the specified [id]. /// /// API docs: https://developer.github.com/v3/gists/#get-a-single-gist Future getGist(String id) => github.getJSON('/gists/$id', - statusCode: StatusCodes.OK, convert: (i) => Gist.fromJson(i)); + statusCode: StatusCodes.OK, convert: Gist.fromJson); /// Creates a Gist /// /// API docs: https://developer.github.com/v3/gists/#create-a-gist Future createGist( Map files, { - String description, + String? description, bool public = false, }) { final map = {'files': {}}; @@ -77,7 +76,7 @@ class GistsService extends Service { '/gists', statusCode: 201, body: GitHubJson.encode(map), - convert: (i) => Gist.fromJson(i), + convert: Gist.fromJson, ); } @@ -95,8 +94,8 @@ class GistsService extends Service { /// API docs: https://developer.github.com/v3/gists/#edit-a-gist Future editGist( String id, { - String description, - Map files, + String? description, + Map? files, }) { final map = {}; @@ -116,7 +115,7 @@ class GistsService extends Service { '/gists/$id', statusCode: 200, body: GitHubJson.encode(map), - convert: (i) => Gist.fromJson(i), + convert: Gist.fromJson, ); } @@ -166,8 +165,8 @@ class GistsService extends Service { /// /// API docs: https://developer.github.com/v3/gists/comments/#list-comments-on-a-gist Stream listComments(String gistId) { - return PaginationHelper(github).objects( - 'GET', '/gists/$gistId/comments', (i) => GistComment.fromJson(i)); + return PaginationHelper(github) + .objects('GET', '/gists/$gistId/comments', GistComment.fromJson); } // TODO: Implement getComment: https://developer.github.com/v3/gists/comments/#get-a-single-comment @@ -177,8 +176,7 @@ class GistsService extends Service { /// API docs: https://developer.github.com/v3/gists/comments/#create-a-comment Future createComment(String gistId, CreateGistComment request) { return github.postJSON('/gists/$gistId/comments', - body: GitHubJson.encode(request), - convert: (i) => GistComment.fromJson(i)); + body: GitHubJson.encode(request), convert: GistComment.fromJson); } // TODO: Implement editComment: https://developer.github.com/v3/gists/comments/#edit-a-comment diff --git a/lib/src/common/git_service.dart b/lib/src/common/git_service.dart index a6d9e98c..338dbeba 100644 --- a/lib/src/common/git_service.dart +++ b/lib/src/common/git_service.dart @@ -1,28 +1,28 @@ import 'dart:async'; import 'dart:convert'; + import 'package:github/src/common.dart'; -import 'package:github/src/common/util/pagination.dart'; /// The [GitService] handles communication with git related methods of the /// GitHub API. /// /// API docs: https://developer.github.com/v3/git/blobs/ class GitService extends Service { - const GitService(GitHub github) : super(github); + const GitService(super.github); /// Fetches a blob from [slug] for a given [sha]. /// /// API docs: https://developer.github.com/v3/git/blobs/#get-a-blob - Future getBlob(RepositorySlug slug, String sha) => + Future getBlob(RepositorySlug slug, String? sha) => github.getJSON('/repos/${slug.fullName}/git/blobs/$sha', - convert: (i) => GitBlob.fromJson(i), statusCode: StatusCodes.OK); + convert: GitBlob.fromJson, statusCode: StatusCodes.OK); /// Creates a blob with specified [blob] content. /// /// API docs: https://developer.github.com/v3/git/blobs/#create-a-blob Future createBlob(RepositorySlug slug, CreateGitBlob blob) { return github.postJSON('/repos/${slug.fullName}/git/blobs', - convert: (i) => GitBlob.fromJson(i), + convert: GitBlob.fromJson, statusCode: StatusCodes.CREATED, body: GitHubJson.encode(blob)); } @@ -30,16 +30,16 @@ class GitService extends Service { /// Fetches a commit from [slug] for a given [sha]. /// /// API docs: https://developer.github.com/v3/git/commits/#get-a-commit - Future getCommit(RepositorySlug slug, String sha) => + Future getCommit(RepositorySlug slug, String? sha) => github.getJSON('/repos/${slug.fullName}/git/commits/$sha', - convert: (i) => GitCommit.fromJson(i), statusCode: StatusCodes.OK); + convert: GitCommit.fromJson, statusCode: StatusCodes.OK); /// Creates a new commit in a repository. /// /// API docs: https://developer.github.com/v3/git/commits/#create-a-commit Future createCommit(RepositorySlug slug, CreateGitCommit commit) { return github.postJSON('/repos/${slug.fullName}/git/commits', - convert: (i) => GitCommit.fromJson(i), + convert: GitCommit.fromJson, statusCode: StatusCodes.CREATED, body: GitHubJson.encode(commit)); } @@ -51,7 +51,7 @@ class GitService extends Service { /// API docs: https://developer.github.com/v3/git/refs/#get-a-reference Future getReference(RepositorySlug slug, String ref) => github.getJSON('/repos/${slug.fullName}/git/refs/$ref', - convert: (i) => GitReference.fromJson(i), statusCode: StatusCodes.OK); + convert: GitReference.fromJson, statusCode: StatusCodes.OK); /// Lists the references in a repository. /// @@ -60,14 +60,13 @@ class GitService extends Service { /// by specifying a [type], the most common being "heads" and "tags". /// /// API docs: https://developer.github.com/v3/git/refs/#get-all-references - Stream listReferences(RepositorySlug slug, {String type}) { + Stream listReferences(RepositorySlug slug, {String? type}) { var path = '/repos/${slug.fullName}/git/refs'; if (type != null) { path += '/$type'; } - return PaginationHelper(github) - .objects('GET', path, (i) => GitReference.fromJson(i)); + return PaginationHelper(github).objects('GET', path, GitReference.fromJson); } /// Creates a new reference in a repository. @@ -77,9 +76,9 @@ class GitService extends Service { /// /// API docs: https://developer.github.com/v3/git/refs/#create-a-reference Future createReference( - RepositorySlug slug, String ref, String sha) { + RepositorySlug slug, String ref, String? sha) { return github.postJSON('/repos/${slug.fullName}/git/refs', - convert: (i) => GitReference.fromJson(i), + convert: GitReference.fromJson, statusCode: StatusCodes.CREATED, body: GitHubJson.encode({'ref': ref, 'sha': sha})); } @@ -90,7 +89,7 @@ class GitService extends Service { Future editReference( RepositorySlug slug, String ref, - String sha, { + String? sha, { bool force = false, }) { final body = GitHubJson.encode({'sha': sha, 'force': force}); @@ -118,16 +117,16 @@ class GitService extends Service { /// Fetches a tag from the repo given a SHA. /// /// API docs: https://developer.github.com/v3/git/tags/#get-a-tag - Future getTag(RepositorySlug slug, String sha) => + Future getTag(RepositorySlug slug, String? sha) => github.getJSON('/repos/${slug.fullName}/git/tags/$sha', - convert: (i) => GitTag.fromJson(i), statusCode: StatusCodes.OK); + convert: GitTag.fromJson, statusCode: StatusCodes.OK); /// Creates a new tag in a repository. /// /// API docs: https://developer.github.com/v3/git/tags/#create-a-tag-object Future createTag(RepositorySlug slug, CreateGitTag tag) => github.postJSON('/repos/${slug.fullName}/git/tags', - convert: (i) => GitTag.fromJson(i), + convert: GitTag.fromJson, statusCode: StatusCodes.CREATED, body: GitHubJson.encode(tag)); @@ -137,7 +136,7 @@ class GitService extends Service { /// /// API docs: https://developer.github.com/v3/git/trees/#get-a-tree /// and https://developer.github.com/v3/git/trees/#get-a-tree-recursively - Future getTree(RepositorySlug slug, String sha, + Future getTree(RepositorySlug slug, String? sha, {bool recursive = false}) { var path = '/repos/${slug.fullName}/git/trees/$sha'; if (recursive) { @@ -145,7 +144,7 @@ class GitService extends Service { } return github.getJSON(path, - convert: (j) => GitTree.fromJson(j), statusCode: StatusCodes.OK); + convert: GitTree.fromJson, statusCode: StatusCodes.OK); } /// Creates a new tree in a repository. @@ -153,7 +152,7 @@ class GitService extends Service { /// API docs: https://developer.github.com/v3/git/trees/#create-a-tree Future createTree(RepositorySlug slug, CreateGitTree tree) { return github.postJSON('/repos/${slug.fullName}/git/trees', - convert: (j) => GitTree.fromJson(j), + convert: GitTree.fromJson, statusCode: StatusCodes.CREATED, body: GitHubJson.encode(tree)); } diff --git a/lib/src/common/github.dart b/lib/src/common/github.dart index 27e36c5e..e6ba64cb 100644 --- a/lib/src/common/github.dart +++ b/lib/src/common/github.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:convert'; + import 'package:github/src/common.dart'; -import 'package:github/src/common/util/utils.dart'; import 'package:http/http.dart' as http; import 'package:http_parser/http_parser.dart' as http_parser; import 'package:meta/meta.dart'; @@ -19,38 +19,50 @@ class GitHub { /// [endpoint] is the api endpoint to use /// [auth] is the authentication information GitHub({ - Authentication auth, + this.auth = const Authentication.anonymous(), this.endpoint = 'https://api.github.com', - http.Client client, - }) : auth = auth ?? Authentication.anonymous(), - client = client ?? http.Client(); + this.version = '2022-11-28', + http.Client? client, + }) : client = client ?? http.Client(); static const _ratelimitLimitHeader = 'x-ratelimit-limit'; static const _ratelimitResetHeader = 'x-ratelimit-reset'; static const _ratelimitRemainingHeader = 'x-ratelimit-remaining'; + @visibleForTesting + static const versionHeader = 'X-GitHub-Api-Version'; + /// Authentication Information Authentication auth; /// API Endpoint final String endpoint; + /// Calendar version of the GitHub API to use. + /// + /// Changing this value is unsupported. However, it may unblock you if there's + /// hotfix versions. + /// + /// See also: + /// * https://docs.github.com/en/rest/overview/api-versions?apiVersion=2022-11-28 + final String version; + /// HTTP Client final http.Client client; - ActivityService _activity; - AuthorizationsService _authorizations; - GistsService _gists; - GitService _git; - IssuesService _issues; - MiscService _misc; - OrganizationsService _organizations; - PullRequestsService _pullRequests; - RepositoriesService _repositories; - SearchService _search; - UrlShortenerService _urlShortener; - UsersService _users; - ChecksService _checks; + ActivityService? _activity; + AuthorizationsService? _authorizations; + GistsService? _gists; + GitService? _git; + IssuesService? _issues; + MiscService? _misc; + OrganizationsService? _organizations; + PullRequestsService? _pullRequests; + RepositoriesService? _repositories; + SearchService? _search; + UrlShortenerService? _urlShortener; + UsersService? _users; + ChecksService? _checks; /// The maximum number of requests that the consumer is permitted to make per /// hour. @@ -58,26 +70,26 @@ class GitHub { /// Updated with every request. /// /// Will be `null` if no requests have been made yet. - int get rateLimitLimit => _rateLimitLimit; + int? get rateLimitLimit => _rateLimitLimit; /// The number of requests remaining in the current rate limit window. /// /// Updated with every request. /// /// Will be `null` if no requests have been made yet. - int get rateLimitRemaining => _rateLimitRemaining; + int? get rateLimitRemaining => _rateLimitRemaining; /// The time at which the current rate limit window resets. /// /// Updated with every request. /// /// Will be `null` if no requests have been made yet. - DateTime get rateLimitReset => _rateLimitReset == null + DateTime? get rateLimitReset => _rateLimitReset == null ? null - : DateTime.fromMillisecondsSinceEpoch(_rateLimitReset * 1000, + : DateTime.fromMillisecondsSinceEpoch(_rateLimitReset! * 1000, isUtc: true); - int _rateLimitReset, _rateLimitLimit, _rateLimitRemaining; + int? _rateLimitReset, _rateLimitLimit, _rateLimitRemaining; /// Service for activity related methods of the GitHub API. ActivityService get activity => _activity ??= ActivityService(this); @@ -143,12 +155,12 @@ class GitHub { /// The default [convert] function returns the input object. Future getJSON( String path, { - int statusCode, - void Function(http.Response response) fail, - Map headers, - Map params, - JSONConverter convert, - String preview, + int? statusCode, + void Function(http.Response response)? fail, + Map? headers, + Map? params, + JSONConverter? convert, + String? preview, }) => requestJson( 'GET', @@ -178,18 +190,18 @@ class GitHub { /// /// The future will pass the object returned from this function to the then method. /// The default [convert] function returns the input object. - /// [body] is the data to send to the server. Pass in a List if you want to post binary body data. Everything else will have .toString() called on it and set as text content + /// [body] is the data to send to the server. Pass in a `List` if you want to post binary body data. Everything else will have .toString() called on it and set as text content /// [S] represents the input type. /// [T] represents the type return from this function after conversion Future postJSON( String path, { - int statusCode, - void Function(http.Response response) fail, - Map headers, - Map params, - JSONConverter convert, + int? statusCode, + void Function(http.Response response)? fail, + Map? headers, + Map? params, + JSONConverter? convert, dynamic body, - String preview, + String? preview, }) => requestJson( 'POST', @@ -220,18 +232,18 @@ class GitHub { /// /// The future will pass the object returned from this function to the then method. /// The default [convert] function returns the input object. - /// [body] is the data to send to the server. Pass in a List if you want to post binary body data. Everything else will have .toString() called on it and set as text content + /// [body] is the data to send to the server. Pass in a `List` if you want to post binary body data. Everything else will have .toString() called on it and set as text content /// [S] represents the input type. /// [T] represents the type return from this function after conversion Future putJSON( String path, { - int statusCode, - void Function(http.Response response) fail, - Map headers, - Map params, - JSONConverter convert, + int? statusCode, + void Function(http.Response response)? fail, + Map? headers, + Map? params, + JSONConverter? convert, dynamic body, - String preview, + String? preview, }) => requestJson( 'PUT', @@ -245,18 +257,60 @@ class GitHub { preview: preview, ); + /// Handles PATCH Requests that respond with JSON + /// + /// [path] can either be a path like '/repos' or a full url. + /// [statusCode] is the expected status code. If it is null, it is ignored. + /// If the status code that the response returns is not the status code you provide + /// then the [fail] function will be called with the HTTP Response. + /// + /// If you don't throw an error or break out somehow, it will go into some error checking + /// that throws exceptions when it finds a 404 or 401. If it doesn't find a general HTTP Status Code + /// for errors, it throws an Unknown Error. + /// + /// [headers] are HTTP Headers. If it doesn't exist, the 'Accept' and 'Authorization' headers are added. + /// [params] are query string parameters. + /// [convert] is a simple function that is passed this [GitHub] instance and a JSON object. + /// + /// The future will pass the object returned from this function to the then method. + /// The default [convert] function returns the input object. + /// [body] is the data to send to the server. Pass in a `List` if you want to post binary body data. Everything else will have .toString() called on it and set as text content + /// [S] represents the input type. + /// [T] represents the type return from this function after conversion + Future patchJSON( + String path, { + int? statusCode, + void Function(http.Response response)? fail, + Map? headers, + Map? params, + JSONConverter? convert, + dynamic body, + String? preview, + }) => + requestJson( + 'PATCH', + path, + statusCode: statusCode, + fail: fail, + headers: headers, + params: params, + convert: convert, + body: body, + preview: preview, + ); + Future requestJson( String method, String path, { - int statusCode, - void Function(http.Response response) fail, - Map headers, - Map params, - JSONConverter convert, + int? statusCode, + void Function(http.Response response)? fail, + Map? headers, + Map? params, + JSONConverter? convert, dynamic body, - String preview, + String? preview, }) async { - convert ??= (input) => input as T; + convert ??= (input) => input as T?; headers ??= {}; if (preview != null) { @@ -264,6 +318,7 @@ class GitHub { } headers.putIfAbsent('Accept', () => v3ApiMimeType); + headers.putIfAbsent(versionHeader, () => version); final response = await request( method, @@ -277,12 +332,7 @@ class GitHub { final json = jsonDecode(response.body); - if (convert == null) { - _applyExpandos(json, response); - return json; - } - - final returnValue = convert(json); + final returnValue = convert(json) as T; _applyExpandos(returnValue, response); return returnValue; } @@ -293,39 +343,39 @@ class GitHub { /// [path] can either be a path like '/repos' or a full url. /// [headers] are HTTP Headers. If it doesn't exist, the 'Accept' and 'Authorization' headers are added. /// [params] are query string parameters. - /// [body] is the body content of requests that take content. Pass in a List if you want to post binary body data. Everything else will have .toString() called on it and set as text content + /// [body] is the body content of requests that take content. Pass in a `List` if you want to post binary body data. Everything else will have .toString() called on it and set as text content /// Future request( String method, String path, { - Map headers, - Map params, + Map? headers, + Map? params, dynamic body, - int statusCode, - void Function(http.Response response) fail, - String preview, + int? statusCode, + void Function(http.Response response)? fail, + String? preview, }) async { - if (rateLimitRemaining != null && rateLimitRemaining <= 0) { + if (rateLimitRemaining != null && rateLimitRemaining! <= 0) { assert(rateLimitReset != null); final now = DateTime.now(); - final waitTime = rateLimitReset.difference(now); + final waitTime = rateLimitReset!.difference(now); await Future.delayed(waitTime); } - headers ??= {}; + headers ??= {}; if (preview != null) { headers['Accept'] = preview; } - if (auth.isToken) { - headers.putIfAbsent('Authorization', () => 'token ${auth.token}'); - } else if (auth.isBasic) { - final userAndPass = - base64Encode(utf8.encode('${auth.username}:${auth.password}')); - headers.putIfAbsent('Authorization', () => 'basic $userAndPass'); + final authHeaderValue = auth.authorizationHeaderValue(); + if (authHeaderValue != null) { + headers.putIfAbsent('Authorization', () => authHeaderValue); } + // See https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#user-agent-required + headers.putIfAbsent('User-Agent', () => auth.username ?? 'github.dart'); + if (method == 'PUT' && body == null) { headers.putIfAbsent('Content-Length', () => '0'); } @@ -378,27 +428,29 @@ class GitHub { /// /// Internal method to handle status codes /// - @alwaysThrows - void handleStatusCode(http.Response response) { - String message; - List> errors; - if (response.headers['content-type'].contains('application/json')) { - final json = jsonDecode(response.body); - message = json['message']; - if (json['errors'] != null) { - try { - errors = List>.from(json['errors']); - } catch (_) { - errors = [ - {'code': json['errors'].toString()} - ]; + Never handleStatusCode(http.Response response) { + String? message = ''; + List>? errors; + if (response.headers['content-type']!.contains('application/json')) { + try { + final json = jsonDecode(response.body); + message = json['message']; + if (json['errors'] != null) { + try { + errors = List>.from(json['errors']); + } catch (_) { + errors = [ + {'code': json['errors'].toString()} + ]; + } } + } catch (ex) { + throw UnknownError(this, ex.toString()); } } switch (response.statusCode) { case 404: throw NotFound(this, 'Requested Resource was Not Found'); - break; case 401: throw AccessForbidden(this); case 400: @@ -409,7 +461,6 @@ class GitHub { } else { throw BadRequest(this); } - break; case 422: final buff = StringBuffer(); buff.writeln(); @@ -438,32 +489,28 @@ class GitHub { /// Disposes of this GitHub Instance. /// No other methods on this instance should be called after this method is called. void dispose() { - // Destroy the Authentication Information - // This is needed for security reasons. - auth = null; - // Closes the HTTP Client client.close(); } void _updateRateLimit(Map headers) { if (headers.containsKey(_ratelimitLimitHeader)) { - _rateLimitLimit = int.parse(headers[_ratelimitLimitHeader]); - _rateLimitRemaining = int.parse(headers[_ratelimitRemainingHeader]); - _rateLimitReset = int.parse(headers[_ratelimitResetHeader]); + _rateLimitLimit = int.parse(headers[_ratelimitLimitHeader]!); + _rateLimitRemaining = int.parse(headers[_ratelimitRemainingHeader]!); + _rateLimitReset = int.parse(headers[_ratelimitResetHeader]!); } } } -void _applyExpandos(Object target, http.Response response) { +void _applyExpandos(dynamic target, http.Response response) { _etagExpando[target] = response.headers['etag']; if (response.headers['date'] != null) { - _dateExpando[target] = http_parser.parseHttpDate(response.headers['date']); + _dateExpando[target] = http_parser.parseHttpDate(response.headers['date']!); } } final _etagExpando = Expando('etag'); final _dateExpando = Expando('date'); -String getResponseEtag(Object obj) => _etagExpando[obj]; -DateTime getResponseDate(Object obj) => _dateExpando[obj]; +String? getResponseEtag(Object obj) => _etagExpando[obj]; +DateTime? getResponseDate(Object obj) => _dateExpando[obj]; diff --git a/lib/src/common/issues_service.dart b/lib/src/common/issues_service.dart index db412d17..ed061846 100644 --- a/lib/src/common/issues_service.dart +++ b/lib/src/common/issues_service.dart @@ -2,28 +2,26 @@ import 'dart:async'; import 'dart:convert'; import 'package:github/src/common.dart'; -import 'package:github/src/common/model/users.dart'; -import 'package:github/src/common/util/pagination.dart'; /// The [IssuesService] handles communication with issues related methods of the /// GitHub API. /// /// API docs: https://developer.github.com/v3/issues/ class IssuesService extends Service { - IssuesService(GitHub github) : super(github); + IssuesService(super.github); /// List all issues across all the authenticated user’s visible repositories /// including owned repositories, member repositories, and organization repositories /// /// API docs: https://developer.github.com/v3/issues/#list-issues Stream listAll( - {int milestoneNumber, - String state, - String direction, - String sort, - DateTime since, - int perPage, - List labels}) { + {int? milestoneNumber, + String? state, + String? direction, + String? sort, + DateTime? since, + int? perPage, + List? labels}) { return _listIssues('/issues', milestoneNumber, state, direction, sort, since, perPage, labels); } @@ -33,13 +31,13 @@ class IssuesService extends Service { /// /// API docs: https://developer.github.com/v3/issues/#list-issues Stream listByUser( - {int milestoneNumber, - String state, - String direction, - String sort, - DateTime since, - int perPage, - List labels}) { + {int? milestoneNumber, + String? state, + String? direction, + String? sort, + DateTime? since, + int? perPage, + List? labels}) { return _listIssues('/user/issues', milestoneNumber, state, direction, sort, since, perPage, labels); } @@ -48,13 +46,13 @@ class IssuesService extends Service { /// /// API docs: https://developer.github.com/v3/issues/#list-issues Stream listByOrg(String org, - {int milestoneNumber, - String state, - String direction, - String sort, - DateTime since, - int perPage, - List labels}) { + {int? milestoneNumber, + String? state, + String? direction, + String? sort, + DateTime? since, + int? perPage, + List? labels}) { return _listIssues('/orgs/$org/issues', milestoneNumber, state, direction, sort, since, perPage, labels); } @@ -65,26 +63,26 @@ class IssuesService extends Service { /// /// API docs:https://developer.github.com/v3/issues/#list-issues-for-a-repository Stream listByRepo(RepositorySlug slug, - {int milestoneNumber, - String state, - String direction, - String sort, - DateTime since, - int perPage, - List labels}) { + {int? milestoneNumber, + String? state, + String? direction, + String? sort, + DateTime? since, + int? perPage, + List? labels}) { return _listIssues('/repos/${slug.fullName}/issues', milestoneNumber, state, direction, sort, since, perPage, labels); } Stream _listIssues( String pathSegment, - int milestoneNumber, - String state, - String direction, - String sort, - DateTime since, - int perPage, - List labels) { + int? milestoneNumber, + String? state, + String? direction, + String? sort, + DateTime? since, + int? perPage, + List? labels) { final params = {}; if (perPage != null) { @@ -125,7 +123,7 @@ class IssuesService extends Service { return PaginationHelper(github).objects( 'GET', pathSegment, - (i) => Issue.fromJson(i), + Issue.fromJson, params: params, ); } @@ -141,12 +139,12 @@ class IssuesService extends Service { /// /// See https://developer.github.com/v3/reactions/ Stream listReactions(RepositorySlug slug, int issueNumber, - {ReactionType content}) { + {ReactionType? content}) { var query = content != null ? '?content=${content.content}' : ''; return PaginationHelper(github).objects( 'GET', '/repos/${slug.owner}/${slug.name}/issues/$issueNumber/reactions$query', - (i) => Reaction.fromJson(i), + Reaction.fromJson, headers: { 'Accept': 'application/vnd.github.squirrel-girl-preview+json', }, @@ -171,7 +169,7 @@ class IssuesService extends Service { /// API docs: https://developer.github.com/v3/issues/#get-a-single-issue Future get(RepositorySlug slug, int issueNumber) => github.getJSON('/repos/${slug.fullName}/issues/$issueNumber', - convert: (i) => Issue.fromJson(i)); + convert: Issue.fromJson); /// Create an issue. /// @@ -196,8 +194,8 @@ class IssuesService extends Service { /// /// API docs: https://developer.github.com/v3/issues/assignees/#list-assignees Stream listAssignees(RepositorySlug slug) { - return PaginationHelper(github).objects( - 'GET', '/repos/${slug.fullName}/assignees', (i) => User.fromJson(i)); + return PaginationHelper(github) + .objects('GET', '/repos/${slug.fullName}/assignees', User.fromJson); } /// Checks if a user is an assignee for the specified repository. @@ -217,17 +215,15 @@ class IssuesService extends Service { return PaginationHelper(github).objects( 'GET', '/repos/${slug.fullName}/issues/$issueNumber/comments', - (i) => IssueComment.fromJson(i)); + IssueComment.fromJson); } /// Lists all comments in a repository. /// /// API docs: https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue Stream listCommentsByRepo(RepositorySlug slug) { - return PaginationHelper(github).objects( - 'GET', - '/repos/${slug.fullName}/issues/comments', - (i) => IssueComment.fromJson(i)); + return PaginationHelper(github).objects('GET', + '/repos/${slug.fullName}/issues/comments', IssueComment.fromJson); } /// Fetches the specified issue comment. @@ -235,7 +231,7 @@ class IssuesService extends Service { /// API docs: https://developer.github.com/v3/issues/comments/#get-a-single-comment Future getComment(RepositorySlug slug, int id) => github.getJSON('/repos/${slug.fullName}/issues/comments/$id', - convert: (i) => IssueComment.fromJson(i)); + convert: IssueComment.fromJson); /// Creates a new comment on the specified issue /// @@ -246,12 +242,23 @@ class IssuesService extends Service { return github.postJSON( '/repos/${slug.fullName}/issues/$issueNumber/comments', body: it, - convert: (i) => IssueComment.fromJson(i), + convert: IssueComment.fromJson, statusCode: StatusCodes.CREATED, ); } - // TODO: Implement editComment: https://developer.github.com/v3/issues/comments/#edit-a-comment + /// Update an issue comment. + /// + /// API docs: https://docs.github.com/en/rest/reference/issues#update-an-issue-comment + Future updateComment(RepositorySlug slug, int id, String body) { + final it = GitHubJson.encode({'body': body}); + return github.postJSON( + '/repos/${slug.fullName}/issues/comments/$id', + body: it, + convert: IssueComment.fromJson, + statusCode: StatusCodes.OK, + ); + } /// Deletes an issue comment. /// @@ -268,8 +275,8 @@ class IssuesService extends Service { /// /// API docs: https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository Stream listLabels(RepositorySlug slug) { - return PaginationHelper(github).objects( - 'GET', '/repos/${slug.fullName}/labels', (i) => IssueLabel.fromJson(i)); + return PaginationHelper(github) + .objects('GET', '/repos/${slug.fullName}/labels', IssueLabel.fromJson); } /// Fetches a single label. @@ -277,25 +284,55 @@ class IssuesService extends Service { /// API docs: https://developer.github.com/v3/issues/labels/#get-a-single-label Future getLabel(RepositorySlug slug, String name) => github.getJSON('/repos/${slug.fullName}/labels/$name', - convert: (i) => IssueLabel.fromJson(i), statusCode: StatusCodes.OK); + convert: IssueLabel.fromJson, statusCode: StatusCodes.OK); /// Creates a new label on the specified repository. /// /// API docs: https://developer.github.com/v3/issues/labels/#create-a-label Future createLabel( - RepositorySlug slug, String name, String color) { - return github.postJSON('/repos/${slug.fullName}/labels', - body: GitHubJson.encode({'name': name, 'color': color}), - convert: (i) => IssueLabel.fromJson(i)); + RepositorySlug slug, + String name, { + String? color, + String? description, + }) { + return github.postJSON( + '/repos/${slug.fullName}/labels', + body: GitHubJson.encode({ + 'name': name, + if (color != null) 'color': color, + if (description != null) 'description': description, + }), + convert: IssueLabel.fromJson, + ); } /// Edits a label. /// /// API docs: https://developer.github.com/v3/issues/labels/#update-a-label + @Deprecated('See updateLabel instead.') Future editLabel(RepositorySlug slug, String name, String color) { - return github.postJSON('/repos/${slug.fullName}/labels/$name', - body: GitHubJson.encode({'name': name, 'color': color}), - convert: (i) => IssueLabel.fromJson(i)); + return updateLabel(slug, name, color: color); + } + + /// Update a label. + /// + /// API docs: https://developer.github.com/v3/issues/labels/#update-a-label + Future updateLabel( + RepositorySlug slug, + String name, { + String? newName, + String? color, + String? description, + }) { + return github.patchJSON( + '/repos/${slug.fullName}/labels/$name', + body: GitHubJson.encode({ + if (newName != null) 'new_name': newName, + if (color != null) 'color': color, + if (description != null) 'description': description, + }), + convert: IssueLabel.fromJson, + ); } /// Deletes a label. @@ -315,7 +352,7 @@ class IssuesService extends Service { return PaginationHelper(github).objects( 'GET', '/repos/${slug.fullName}/issues/$issueNumber/labels', - (i) => IssueLabel.fromJson(i)); + IssueLabel.fromJson); } /// Adds labels to an issue. @@ -326,10 +363,8 @@ class IssuesService extends Service { return github.postJSON, List>( '/repos/${slug.fullName}/issues/$issueNumber/labels', body: GitHubJson.encode(labels), - convert: (input) => input - .cast>() - .map((i) => IssueLabel.fromJson(i)) - .toList(), + convert: (input) => + input.cast>().map(IssueLabel.fromJson).toList(), ); } @@ -342,8 +377,7 @@ class IssuesService extends Service { .request('PUT', '/repos/${slug.fullName}/issues/$issueNumber/labels', body: GitHubJson.encode(labels)) .then((response) { - return jsonDecode(response.body) - .map((Map it) => IssueLabel.fromJson(it)); + return jsonDecode(response.body).map(IssueLabel.fromJson); }); } @@ -373,8 +407,8 @@ class IssuesService extends Service { /// /// API docs: https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository Stream listMilestones(RepositorySlug slug) { - return PaginationHelper(github).objects('GET', - '/repos/${slug.fullName}/milestones', (i) => Milestone.fromJson(i)); + return PaginationHelper(github).objects( + 'GET', '/repos/${slug.fullName}/milestones', Milestone.fromJson); } // TODO: Implement getMilestone: https://developer.github.com/v3/issues/milestones/#get-a-single-milestone @@ -385,8 +419,7 @@ class IssuesService extends Service { Future createMilestone( RepositorySlug slug, CreateMilestone request) { return github.postJSON('/repos/${slug.fullName}/milestones', - body: GitHubJson.encode(request), - convert: (i) => Milestone.fromJson(i)); + body: GitHubJson.encode(request), convert: Milestone.fromJson); } // TODO: Implement editMilestone: https://developer.github.com/v3/issues/milestones/#update-a-milestone @@ -399,4 +432,41 @@ class IssuesService extends Service { .request('DELETE', '/repos/${slug.fullName}/milestones/$number') .then((response) => response.statusCode == StatusCodes.NO_CONTENT); } + + /// Lists all timeline events for an issue. + /// + /// API docs: https://docs.github.com/en/rest/issues/timeline?apiVersion=2022-11-28 + Stream listTimeline(RepositorySlug slug, int issueNumber) { + return PaginationHelper(github).objects( + 'GET', + '/repos/${slug.fullName}/issues/$issueNumber/timeline', + TimelineEvent.fromJson, + ); + } + + /// Lock an issue. + /// + /// API docs: https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#lock-an-issue + /// + /// The `lockReason`, if specified, must be one of: `off-topic`, `too heated`, `resolved`, `spam`. + Future lock(RepositorySlug slug, int number, + {String? lockReason}) async { + String body; + if (lockReason != null) { + body = GitHubJson.encode({'lock_reason': lockReason}); + } else { + body = '{}'; + } + await github.postJSON('/repos/${slug.fullName}/issues/$number/lock', + body: body, statusCode: 204); + } + + /// Unlock an issue. + /// + /// API docs: https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#unlock-an-issue + Future unlock(RepositorySlug slug, int number) async { + await github.request( + 'DELETE', '/repos/${slug.fullName}/issues/$number/lock', + statusCode: 204); + } } diff --git a/lib/src/common/misc_service.dart b/lib/src/common/misc_service.dart index 9ccc289b..30385a18 100644 --- a/lib/src/common/misc_service.dart +++ b/lib/src/common/misc_service.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:github/src/common.dart'; /// The [MiscService] handles communication with misc related methods of the @@ -6,7 +7,7 @@ import 'package:github/src/common.dart'; /// /// API docs: https://developer.github.com/v3/misc/ class MiscService extends Service { - MiscService(GitHub github) : super(github); + MiscService(super.github); /// Fetches all emojis available on GitHub /// Returns a map of the name to a url of the image. @@ -34,7 +35,7 @@ class MiscService extends Service { /// API docs: https://developer.github.com/v3/gitignore/#get-a-single-template Future getGitignoreTemplate(String name) => github.getJSON('/gitignore/templates/$name', - convert: (i) => GitignoreTemplate.fromJson(i)); + convert: GitignoreTemplate.fromJson); /// Renders Markdown from the [input]. /// @@ -42,8 +43,8 @@ class MiscService extends Service { /// [context] is the repository context. Only take into account when [mode] is 'gfm'. /// /// API docs: https://developer.github.com/v3/markdown/#render-an-arbitrary-markdown-document - Future renderMarkdown(String input, - {String mode = 'markdown', String context}) { + Future renderMarkdown(String? input, + {String mode = 'markdown', String? context}) { return github .request('POST', '/markdown', body: GitHubJson.encode( @@ -61,18 +62,20 @@ class MiscService extends Service { /// /// API docs: https://developer.github.com/v3/rate_limit/ Future getRateLimit() { - return github.request('GET', '/').then((response) { - return RateLimit.fromHeaders(response.headers); + return github.request('GET', '/rate_limit').then((response) { + return RateLimit.fromRateLimitResponse(jsonDecode(response.body)); }); } /// Gets the GitHub API Status. + /// + /// API docs: https://www.githubstatus.com/api Future getApiStatus() => - github.getJSON('https://status.github.com/api/status.json', - statusCode: StatusCodes.OK, convert: (i) => APIStatus.fromJson(i)); + github.getJSON('https://status.github.com/api/v2/status.json', + statusCode: StatusCodes.OK, convert: APIStatus.fromJson); /// Returns an ASCII Octocat with the specified [text]. - Future getOctocat([String text]) { + Future getOctocat([String? text]) { final params = {}; if (text != null) { @@ -92,7 +95,7 @@ class MiscService extends Service { } class Octocat { - String name; - String image; - String url; + String? name; + String? image; + String? url; } diff --git a/lib/src/common/model/activity.dart b/lib/src/common/model/activity.dart index 831cc397..cdb873ec 100644 --- a/lib/src/common/model/activity.dart +++ b/lib/src/common/model/activity.dart @@ -1,5 +1,4 @@ import 'package:github/src/common.dart'; -import 'package:github/src/common/model/users.dart'; import 'package:json_annotation/json_annotation.dart'; part 'activity.g.dart'; @@ -16,15 +15,15 @@ class Event { this.payload, this.createdAt, }); - String id; - String type; - Repository repo; - User actor; - Organization org; - Map payload; + String? id; + String? type; + Repository? repo; + User? actor; + Organization? org; + Map? payload; @JsonKey(name: 'created_at') - DateTime createdAt; + DateTime? createdAt; factory Event.fromJson(Map input) => _$EventFromJson(input); Map toJson() => _$EventToJson(this); @@ -39,12 +38,12 @@ class RepositorySubscription { this.reason, this.createdAt, }); - bool subscribed; - bool ignored; - String reason; + bool? subscribed; + bool? ignored; + String? reason; @JsonKey(name: 'created_at') - DateTime createdAt; + DateTime? createdAt; factory RepositorySubscription.fromJson(Map input) => _$RepositorySubscriptionFromJson(input); diff --git a/lib/src/common/model/activity.g.dart b/lib/src/common/model/activity.g.dart index 02da2f73..9638fc87 100644 --- a/lib/src/common/model/activity.g.dart +++ b/lib/src/common/model/activity.g.dart @@ -6,25 +6,23 @@ part of 'activity.dart'; // JsonSerializableGenerator // ************************************************************************** -Event _$EventFromJson(Map json) { - return Event( - id: json['id'] as String, - type: json['type'] as String, - repo: json['repo'] == null - ? null - : Repository.fromJson(json['repo'] as Map), - actor: json['actor'] == null - ? null - : User.fromJson(json['actor'] as Map), - org: json['org'] == null - ? null - : Organization.fromJson(json['org'] as Map), - payload: json['payload'] as Map, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - ); -} +Event _$EventFromJson(Map json) => Event( + id: json['id'] as String?, + type: json['type'] as String?, + repo: json['repo'] == null + ? null + : Repository.fromJson(json['repo'] as Map), + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + org: json['org'] == null + ? null + : Organization.fromJson(json['org'] as Map), + payload: json['payload'] as Map?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + ); Map _$EventToJson(Event instance) => { 'id': instance.id, @@ -37,16 +35,15 @@ Map _$EventToJson(Event instance) => { }; RepositorySubscription _$RepositorySubscriptionFromJson( - Map json) { - return RepositorySubscription( - subscribed: json['subscribed'] as bool, - ignored: json['ignored'] as bool, - reason: json['reason'] as String, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - ); -} + Map json) => + RepositorySubscription( + subscribed: json['subscribed'] as bool?, + ignored: json['ignored'] as bool?, + reason: json['reason'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + ); Map _$RepositorySubscriptionToJson( RepositorySubscription instance) => diff --git a/lib/src/common/model/authorizations.dart b/lib/src/common/model/authorizations.dart index 3cd815f0..fa80708e 100644 --- a/lib/src/common/model/authorizations.dart +++ b/lib/src/common/model/authorizations.dart @@ -5,7 +5,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'authorizations.g.dart'; /// Model class for an authorization. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class Authorization { Authorization( {this.id, @@ -18,15 +18,15 @@ class Authorization { this.updatedAt, this.user}); - int id; - List scopes; - String token; - AuthorizationApplication app; - String note; - String noteUrl; - DateTime createdAt; - DateTime updatedAt; - User user; + int? id; + List? scopes; + String? token; + AuthorizationApplication? app; + String? note; + String? noteUrl; + DateTime? createdAt; + DateTime? updatedAt; + User? user; factory Authorization.fromJson(Map input) => _$AuthorizationFromJson(input); @@ -34,29 +34,29 @@ class Authorization { } /// Model class for an application of an [Authorization]. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class AuthorizationApplication { AuthorizationApplication({this.url, this.name, this.clientId}); - String url; - String name; - String clientId; + String? url; + String? name; + String? clientId; factory AuthorizationApplication.fromJson(Map input) => _$AuthorizationApplicationFromJson(input); Map toJson() => _$AuthorizationApplicationToJson(this); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateAuthorization { CreateAuthorization(this.note, {this.scopes, this.noteUrl, this.clientId, this.clientSecret}); - String note; - List scopes; - String noteUrl; - String clientId; - String clientSecret; + String? note; + List? scopes; + String? noteUrl; + String? clientId; + String? clientSecret; factory CreateAuthorization.fromJson(Map input) => _$CreateAuthorizationFromJson(input); diff --git a/lib/src/common/model/authorizations.g.dart b/lib/src/common/model/authorizations.g.dart index 5c1fcbbc..918a8a47 100644 --- a/lib/src/common/model/authorizations.g.dart +++ b/lib/src/common/model/authorizations.g.dart @@ -6,28 +6,28 @@ part of 'authorizations.dart'; // JsonSerializableGenerator // ************************************************************************** -Authorization _$AuthorizationFromJson(Map json) { - return Authorization( - id: json['id'] as int, - scopes: (json['scopes'] as List)?.map((e) => e as String)?.toList(), - token: json['token'] as String, - app: json['app'] == null - ? null - : AuthorizationApplication.fromJson( - json['app'] as Map), - note: json['note'] as String, - noteUrl: json['note_url'] as String, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - ); -} +Authorization _$AuthorizationFromJson(Map json) => + Authorization( + id: (json['id'] as num?)?.toInt(), + scopes: + (json['scopes'] as List?)?.map((e) => e as String).toList(), + token: json['token'] as String?, + app: json['app'] == null + ? null + : AuthorizationApplication.fromJson( + json['app'] as Map), + note: json['note'] as String?, + noteUrl: json['note_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + ); Map _$AuthorizationToJson(Authorization instance) => { @@ -43,13 +43,12 @@ Map _$AuthorizationToJson(Authorization instance) => }; AuthorizationApplication _$AuthorizationApplicationFromJson( - Map json) { - return AuthorizationApplication( - url: json['url'] as String, - name: json['name'] as String, - clientId: json['client_id'] as String, - ); -} + Map json) => + AuthorizationApplication( + url: json['url'] as String?, + name: json['name'] as String?, + clientId: json['client_id'] as String?, + ); Map _$AuthorizationApplicationToJson( AuthorizationApplication instance) => @@ -59,15 +58,15 @@ Map _$AuthorizationApplicationToJson( 'client_id': instance.clientId, }; -CreateAuthorization _$CreateAuthorizationFromJson(Map json) { - return CreateAuthorization( - json['note'] as String, - scopes: (json['scopes'] as List)?.map((e) => e as String)?.toList(), - noteUrl: json['note_url'] as String, - clientId: json['client_id'] as String, - clientSecret: json['client_secret'] as String, - ); -} +CreateAuthorization _$CreateAuthorizationFromJson(Map json) => + CreateAuthorization( + json['note'] as String?, + scopes: + (json['scopes'] as List?)?.map((e) => e as String).toList(), + noteUrl: json['note_url'] as String?, + clientId: json['client_id'] as String?, + clientSecret: json['client_secret'] as String?, + ); Map _$CreateAuthorizationToJson( CreateAuthorization instance) => diff --git a/lib/src/common/model/changes.dart b/lib/src/common/model/changes.dart new file mode 100644 index 00000000..edccdbc5 --- /dev/null +++ b/lib/src/common/model/changes.dart @@ -0,0 +1,68 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:meta/meta.dart'; + +part 'changes.g.dart'; + +@immutable +@JsonSerializable() +class Ref { + const Ref(this.from); + final String? from; + + factory Ref.fromJson(Map input) => _$RefFromJson(input); + Map toJson() => _$RefToJson(this); +} + +@immutable +@JsonSerializable() +class Sha { + const Sha(this.from); + final String? from; + + factory Sha.fromJson(Map input) => _$ShaFromJson(input); + Map toJson() => _$ShaToJson(this); +} + +@immutable +@JsonSerializable() +class Base { + const Base(this.ref, this.sha); + final Ref? ref; + final Sha? sha; + + factory Base.fromJson(Map input) => _$BaseFromJson(input); + Map toJson() => _$BaseToJson(this); +} + +@immutable +@JsonSerializable() +class Body { + const Body(this.from); + final String? from; + + factory Body.fromJson(Map input) => _$BodyFromJson(input); + Map toJson() => _$BodyToJson(this); +} + +@immutable +@JsonSerializable() +class Title { + const Title({this.from}); + final String? from; + + factory Title.fromJson(Map input) => _$TitleFromJson(input); + Map toJson() => _$TitleToJson(this); +} + +@immutable +@JsonSerializable() +class Changes { + const Changes(this.base, this.body, this.title); + final Base? base; + final Body? body; + final Title? title; + + factory Changes.fromJson(Map input) => + _$ChangesFromJson(input); + Map toJson() => _$ChangesToJson(this); +} diff --git a/lib/src/common/model/changes.g.dart b/lib/src/common/model/changes.g.dart new file mode 100644 index 00000000..13e97d0e --- /dev/null +++ b/lib/src/common/model/changes.g.dart @@ -0,0 +1,71 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'changes.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Ref _$RefFromJson(Map json) => Ref( + json['from'] as String?, + ); + +Map _$RefToJson(Ref instance) => { + 'from': instance.from, + }; + +Sha _$ShaFromJson(Map json) => Sha( + json['from'] as String?, + ); + +Map _$ShaToJson(Sha instance) => { + 'from': instance.from, + }; + +Base _$BaseFromJson(Map json) => Base( + json['ref'] == null + ? null + : Ref.fromJson(json['ref'] as Map), + json['sha'] == null + ? null + : Sha.fromJson(json['sha'] as Map), + ); + +Map _$BaseToJson(Base instance) => { + 'ref': instance.ref, + 'sha': instance.sha, + }; + +Body _$BodyFromJson(Map json) => Body( + json['from'] as String?, + ); + +Map _$BodyToJson(Body instance) => { + 'from': instance.from, + }; + +Title _$TitleFromJson(Map json) => Title( + from: json['from'] as String?, + ); + +Map _$TitleToJson(Title instance) => { + 'from': instance.from, + }; + +Changes _$ChangesFromJson(Map json) => Changes( + json['base'] == null + ? null + : Base.fromJson(json['base'] as Map), + json['body'] == null + ? null + : Body.fromJson(json['body'] as Map), + json['title'] == null + ? null + : Title.fromJson(json['title'] as Map), + ); + +Map _$ChangesToJson(Changes instance) => { + 'base': instance.base, + 'body': instance.body, + 'title': instance.title, + }; diff --git a/lib/src/common/model/checks.dart b/lib/src/common/model/checks.dart index 83d6c9c3..b7666579 100644 --- a/lib/src/common/model/checks.dart +++ b/lib/src/common/model/checks.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; + +import 'package:github/src/common/model/pulls.dart'; import 'package:github/src/common/util/utils.dart'; import 'package:meta/meta.dart'; @@ -6,9 +9,9 @@ class CheckRunAnnotationLevel extends EnumWithValue { static const warning = CheckRunAnnotationLevel._('warning'); static const failure = CheckRunAnnotationLevel._('failure'); - const CheckRunAnnotationLevel._(String value) : super(value); + const CheckRunAnnotationLevel._(String super.value); - factory CheckRunAnnotationLevel._fromValue(String value) { + factory CheckRunAnnotationLevel._fromValue(String? value) { switch (value) { case 'notice': return notice; @@ -44,17 +47,23 @@ class CheckRunConclusion extends EnumWithValue { static const neutral = CheckRunConclusion._('neutral'); static const cancelled = CheckRunConclusion._('cancelled'); static const timedOut = CheckRunConclusion._('timed_out'); + static const skipped = CheckRunConclusion._('skipped'); static const actionRequired = CheckRunConclusion._('action_required'); + static const empty = CheckRunConclusion._(null); - const CheckRunConclusion._(String value) : super(value); + const CheckRunConclusion._(super.value); - factory CheckRunConclusion._fromValue(String value) { + factory CheckRunConclusion._fromValue(String? value) { + if (value == null || value == 'null') { + return empty; + } for (final level in const [ success, failure, neutral, cancelled, timedOut, + skipped, actionRequired ]) { if (level.value == value) { @@ -70,39 +79,41 @@ class CheckRunStatus extends EnumWithValue { static const queued = CheckRunStatus._('queued'); static const inProgress = CheckRunStatus._('in_progress'); static const completed = CheckRunStatus._('completed'); - const CheckRunStatus._(String value) : super(value); + const CheckRunStatus._(String super.value); } class CheckRunFilter extends EnumWithValue { static const all = CheckRunFilter._('all'); static const latest = CheckRunFilter._('latest'); - const CheckRunFilter._(String value) : super(value); + const CheckRunFilter._(String super.value); } @immutable class CheckRun { - final String name; - final int id; - final String externalId; - final String headSha; - final CheckRunStatus status; - final int checkSuiteId; - final String detailsUrl; + final String? name; + final int? id; + final String? externalId; + final String? headSha; + final CheckRunStatus? status; + final int? checkSuiteId; + final String? detailsUrl; final DateTime startedAt; + final CheckRunConclusion conclusion; const CheckRun._({ - @required this.id, - @required this.externalId, - @required this.headSha, - @required this.status, - @required this.checkSuiteId, - @required this.name, - @required this.detailsUrl, - @required this.startedAt, + required this.id, + required this.externalId, + required this.headSha, + required this.status, + required this.checkSuiteId, + required this.name, + required this.detailsUrl, + required this.startedAt, + required this.conclusion, }); factory CheckRun.fromJson(Map input) { - CheckRunStatus status; + CheckRunStatus? status; for (final s in const [ CheckRunStatus.completed, CheckRunStatus.inProgress, @@ -122,8 +133,30 @@ class CheckRun { checkSuiteId: input['check_suite']['id'], detailsUrl: input['details_url'], startedAt: DateTime.parse(input['started_at']), + conclusion: CheckRunConclusion._fromValue(input['conclusion']), ); } + + Map toJson() { + return { + 'name': name, + 'id': id, + 'external_id': externalId, + 'status': status, + 'head_sha': externalId, + 'check_suite': { + 'id': checkSuiteId, + }, + 'details_url': detailsUrl, + 'started_at': startedAt.toIso8601String(), + 'conclusion': conclusion, + }; + } + + @override + String toString() { + return jsonEncode(toJson()); + } } @immutable @@ -135,34 +168,33 @@ class CheckRunOutput { final String summary; /// The details of the check run. This parameter supports Markdown. - final String text; + final String? text; /// Adds information from your analysis to specific lines of code. /// Annotations are visible on GitHub in the Checks and Files changed tab of the pull request. /// The Checks API limits the number of annotations to a maximum of 50 per API request. /// To create more than 50 annotations, you have to make multiple requests to the Update a check run endpoint. /// Each time you update the check run, annotations are appended to the list of annotations that already exist for the check run. - final List annotations; + final List? annotations; /// Adds images to the output displayed in the GitHub pull request UI. - final List images; + final List? images; const CheckRunOutput({ - @required this.title, - @required this.summary, + required this.title, + required this.summary, this.text, this.annotations, this.images, - }) : assert(title != null), - assert(summary != null); + }); Map toJson() { return createNonNullMap({ 'title': title, 'summary': summary, 'text': text, - 'annotations': annotations?.map((a) => a.toJson())?.toList(), - 'images': images?.map((i) => i.toJson())?.toList(), + 'annotations': annotations?.map((a) => a.toJson()).toList(), + 'images': images?.map((i) => i.toJson()).toList(), }); } } @@ -181,12 +213,12 @@ class CheckRunAnnotation { /// The start column of the annotation. /// Annotations only support start_column and end_column on the same line. /// Omit this parameter if start_line and end_line have different values. - final int startColumn; + final int? startColumn; /// The end column of the annotation. /// Annotations only support start_column and end_column on the same line. /// Omit this parameter if start_line and end_line have different values. - final int endColumn; + final int? endColumn; /// The level of the annotation. /// Can be one of notice, warning, or failure. @@ -202,31 +234,26 @@ class CheckRunAnnotation { /// Details about this annotation. /// The maximum size is 64 KB. - final String rawDetails; + final String? rawDetails; const CheckRunAnnotation({ - @required this.annotationLevel, - @required this.endLine, - @required this.message, - @required this.path, - @required this.startLine, + required this.annotationLevel, + required this.endLine, + required this.message, + required this.path, + required this.startLine, + required this.title, this.startColumn, this.endColumn, - this.title, this.rawDetails, - }) : assert(path != null), - assert(startLine != null), - assert(endLine != null), - assert(annotationLevel != null), - assert(message != null), - assert(startColumn == null || startLine == endLine, + }) : assert(startColumn == null || startLine == endLine, 'Annotations only support start_column and end_column on the same line.'), assert(endColumn == null || startLine == endLine, 'Annotations only support start_column and end_column on the same line.'), assert(title.length <= 255); @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (other is CheckRunAnnotation) { return other.annotationLevel == annotationLevel && other.path == path && @@ -245,9 +272,6 @@ class CheckRunAnnotation { int get hashCode => path.hashCode; factory CheckRunAnnotation.fromJSON(Map input) { - if (input == null) { - return null; - } return CheckRunAnnotation( path: input['path'], startLine: input['start_line'], @@ -286,14 +310,13 @@ class CheckRunImage { final String imageUrl; /// A short image description. - final String caption; + final String? caption; const CheckRunImage({ - @required this.alternativeText, - @required this.imageUrl, + required this.alternativeText, + required this.imageUrl, this.caption, - }) : assert(alternativeText != null), - assert(imageUrl != null); + }); Map toJson() { return createNonNullMap({ @@ -319,13 +342,10 @@ class CheckRunAction { final String identifier; const CheckRunAction({ - @required this.label, - @required this.description, - @required this.identifier, - }) : assert(label != null), - assert(description != null), - assert(identifier != null), - assert(label.length <= 20), + required this.label, + required this.description, + required this.identifier, + }) : assert(label.length <= 20), assert(description.length <= 40), assert(identifier.length <= 20); @@ -338,28 +358,45 @@ class CheckRunAction { } } +/// API docs: https://docs.github.com/en/rest/reference/checks#check-suites @immutable class CheckSuite { - final int id; - final String headSha; + final int? id; + final String? headBranch; + final String? headSha; final CheckRunConclusion conclusion; + final List pullRequests; const CheckSuite({ - @required this.conclusion, - @required this.headSha, - @required this.id, + required this.conclusion, + required this.headBranch, + required this.headSha, + required this.id, + required this.pullRequests, }); factory CheckSuite.fromJson(Map input) { - if (input == null) { - return null; - } + var pullRequestsJson = input['pull_requests'] as List; + var pullRequests = pullRequestsJson + .map((dynamic json) => + PullRequest.fromJson(json as Map)) + .toList(); return CheckSuite( conclusion: CheckRunConclusion._fromValue(input['conclusion']), + headBranch: input['head_branch'], headSha: input['head_sha'], id: input['id'], + pullRequests: pullRequests, ); } + + Map toJson() { + return { + 'conclusion': conclusion, + 'head_sha': headSha, + 'id': id, + }; + } } @immutable @@ -368,17 +405,14 @@ class AutoTriggerChecks { final int appId; /// Set to true to enable automatic creation of CheckSuite events upon pushes to the repository, or false to disable them. - final bool setting; + final bool? setting; const AutoTriggerChecks({ - @required this.appId, + required this.appId, this.setting = true, - }) : assert(appId != null); + }); factory AutoTriggerChecks.fromJson(Map input) { - if (input == null) { - return null; - } return AutoTriggerChecks( appId: input['app_id'], setting: input['setting'], diff --git a/lib/src/common/model/gists.dart b/lib/src/common/model/gists.dart index 09787941..e5360d70 100644 --- a/lib/src/common/model/gists.dart +++ b/lib/src/common/model/gists.dart @@ -5,7 +5,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'gists.g.dart'; /// Model class for gists. -@JsonSerializable(createToJson: false) +@JsonSerializable() class Gist { Gist({ this.id, @@ -21,39 +21,40 @@ class Gist { this.createdAt, this.updatedAt, }); - String id; - String description; - bool public; - User owner; - User user; - List files; + String? id; + String? description; + bool? public; + User? owner; + User? user; + Map? files; @JsonKey(name: 'html_url') - String htmlUrl; + String? htmlUrl; @JsonKey(name: 'comments') - int commentsCount; + int? commentsCount; @JsonKey(name: 'git_pull_url') - String gitPullUrl; + String? gitPullUrl; @JsonKey(name: 'git_push_url') - String gitPushUrl; + String? gitPushUrl; @JsonKey(name: 'created_at') - DateTime createdAt; + DateTime? createdAt; @JsonKey(name: 'updated_at') - DateTime updatedAt; + DateTime? updatedAt; factory Gist.fromJson(Map input) => _$GistFromJson(input); + Map toJson() => _$GistToJson(this); } /// Model class for a gist file. -@JsonSerializable(createToJson: false) +@JsonSerializable() class GistFile { GistFile({ - this.name, + this.filename, this.size, this.rawUrl, this.type, @@ -61,39 +62,40 @@ class GistFile { this.truncated, this.content, }); - String name; - int size; - @JsonKey(name: 'raw_url') - String rawUrl; - String type; - String language; - bool truncated; - String content; + String? filename; + int? size; + String? rawUrl; + String? type; + String? language; + bool? truncated; + String? content; factory GistFile.fromJson(Map input) => _$GistFileFromJson(input); + Map toJson() => _$GistFileToJson(this); } /// Model class for a gist fork. -@JsonSerializable(createToJson: false) +@JsonSerializable() class GistFork { GistFork({this.user, this.id, this.createdAt, this.updatedAt}); - User user; - int id; + User? user; + int? id; @JsonKey(name: 'created_at') - DateTime createdAt; + DateTime? createdAt; @JsonKey(name: 'updated_at') - DateTime updatedAt; + DateTime? updatedAt; factory GistFork.fromJson(Map input) => _$GistForkFromJson(input); + Map toJson() => _$GistForkToJson(this); } /// Model class for a gits history entry. -@JsonSerializable(createToJson: false) +@JsonSerializable() class GistHistoryEntry { GistHistoryEntry({ this.version, @@ -103,28 +105,29 @@ class GistHistoryEntry { this.totalChanges, this.committedAt, }); - String version; + String? version; - User user; + User? user; @JsonKey(name: 'change_status/deletions') - int deletions; + int? deletions; @JsonKey(name: 'change_status/additions') - int additions; + int? additions; @JsonKey(name: 'change_status/total') - int totalChanges; + int? totalChanges; @JsonKey(name: 'committed_at') - DateTime committedAt; + DateTime? committedAt; factory GistHistoryEntry.fromJson(Map input) => _$GistHistoryEntryFromJson(input); + Map toJson() => _$GistHistoryEntryToJson(this); } /// Model class for gist comments. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class GistComment { GistComment({ this.id, @@ -134,11 +137,11 @@ class GistComment { this.body, }); - int id; - User user; - DateTime createdAt; - DateTime updatedAt; - String body; + int? id; + User? user; + DateTime? createdAt; + DateTime? updatedAt; + String? body; factory GistComment.fromJson(Map input) => _$GistCommentFromJson(input); @@ -146,10 +149,10 @@ class GistComment { } /// Model class for a new gist comment to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateGistComment { CreateGistComment(this.body); - String body; + String? body; factory CreateGistComment.fromJson(Map input) => _$CreateGistCommentFromJson(input); diff --git a/lib/src/common/model/gists.g.dart b/lib/src/common/model/gists.g.dart index 5e557b08..3438e5ea 100644 --- a/lib/src/common/model/gists.g.dart +++ b/lib/src/common/model/gists.g.dart @@ -6,91 +6,123 @@ part of 'gists.dart'; // JsonSerializableGenerator // ************************************************************************** -Gist _$GistFromJson(Map json) { - return Gist( - id: json['id'] as String, - description: json['description'] as String, - public: json['public'] as bool, - owner: json['owner'] == null - ? null - : User.fromJson(json['owner'] as Map), - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - files: (json['files'] as List) - ?.map((e) => - e == null ? null : GistFile.fromJson(e as Map)) - ?.toList(), - htmlUrl: json['html_url'] as String, - commentsCount: json['comments'] as int, - gitPullUrl: json['git_pull_url'] as String, - gitPushUrl: json['git_push_url'] as String, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - ); -} +Gist _$GistFromJson(Map json) => Gist( + id: json['id'] as String?, + description: json['description'] as String?, + public: json['public'] as bool?, + owner: json['owner'] == null + ? null + : User.fromJson(json['owner'] as Map), + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + files: (json['files'] as Map?)?.map( + (k, e) => MapEntry(k, GistFile.fromJson(e as Map)), + ), + htmlUrl: json['html_url'] as String?, + commentsCount: (json['comments'] as num?)?.toInt(), + gitPullUrl: json['git_pull_url'] as String?, + gitPushUrl: json['git_push_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + ); -GistFile _$GistFileFromJson(Map json) { - return GistFile( - name: json['name'] as String, - size: json['size'] as int, - rawUrl: json['raw_url'] as String, - type: json['type'] as String, - language: json['language'] as String, - truncated: json['truncated'] as bool, - content: json['content'] as String, - ); -} +Map _$GistToJson(Gist instance) => { + 'id': instance.id, + 'description': instance.description, + 'public': instance.public, + 'owner': instance.owner, + 'user': instance.user, + 'files': instance.files, + 'html_url': instance.htmlUrl, + 'comments': instance.commentsCount, + 'git_pull_url': instance.gitPullUrl, + 'git_push_url': instance.gitPushUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'updated_at': instance.updatedAt?.toIso8601String(), + }; + +GistFile _$GistFileFromJson(Map json) => GistFile( + filename: json['filename'] as String?, + size: (json['size'] as num?)?.toInt(), + rawUrl: json['raw_url'] as String?, + type: json['type'] as String?, + language: json['language'] as String?, + truncated: json['truncated'] as bool?, + content: json['content'] as String?, + ); + +Map _$GistFileToJson(GistFile instance) => { + 'filename': instance.filename, + 'size': instance.size, + 'raw_url': instance.rawUrl, + 'type': instance.type, + 'language': instance.language, + 'truncated': instance.truncated, + 'content': instance.content, + }; + +GistFork _$GistForkFromJson(Map json) => GistFork( + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + id: (json['id'] as num?)?.toInt(), + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + ); -GistFork _$GistForkFromJson(Map json) { - return GistFork( - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - id: json['id'] as int, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - ); -} +Map _$GistForkToJson(GistFork instance) => { + 'user': instance.user, + 'id': instance.id, + 'created_at': instance.createdAt?.toIso8601String(), + 'updated_at': instance.updatedAt?.toIso8601String(), + }; -GistHistoryEntry _$GistHistoryEntryFromJson(Map json) { - return GistHistoryEntry( - version: json['version'] as String, - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - deletions: json['change_status/deletions'] as int, - additions: json['change_status/additions'] as int, - totalChanges: json['change_status/total'] as int, - committedAt: json['committed_at'] == null - ? null - : DateTime.parse(json['committed_at'] as String), - ); -} +GistHistoryEntry _$GistHistoryEntryFromJson(Map json) => + GistHistoryEntry( + version: json['version'] as String?, + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + deletions: (json['change_status/deletions'] as num?)?.toInt(), + additions: (json['change_status/additions'] as num?)?.toInt(), + totalChanges: (json['change_status/total'] as num?)?.toInt(), + committedAt: json['committed_at'] == null + ? null + : DateTime.parse(json['committed_at'] as String), + ); + +Map _$GistHistoryEntryToJson(GistHistoryEntry instance) => + { + 'version': instance.version, + 'user': instance.user, + 'change_status/deletions': instance.deletions, + 'change_status/additions': instance.additions, + 'change_status/total': instance.totalChanges, + 'committed_at': instance.committedAt?.toIso8601String(), + }; -GistComment _$GistCommentFromJson(Map json) { - return GistComment( - id: json['id'] as int, - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - body: json['body'] as String, - ); -} +GistComment _$GistCommentFromJson(Map json) => GistComment( + id: (json['id'] as num?)?.toInt(), + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + body: json['body'] as String?, + ); Map _$GistCommentToJson(GistComment instance) => { @@ -101,11 +133,10 @@ Map _$GistCommentToJson(GistComment instance) => 'body': instance.body, }; -CreateGistComment _$CreateGistCommentFromJson(Map json) { - return CreateGistComment( - json['body'] as String, - ); -} +CreateGistComment _$CreateGistCommentFromJson(Map json) => + CreateGistComment( + json['body'] as String?, + ); Map _$CreateGistCommentToJson(CreateGistComment instance) => { diff --git a/lib/src/common/model/git.dart b/lib/src/common/model/git.dart index 6601b8ab..136c01c4 100644 --- a/lib/src/common/model/git.dart +++ b/lib/src/common/model/git.dart @@ -1,11 +1,10 @@ import 'package:github/src/common.dart'; -import 'package:github/src/common/model/users.dart'; import 'package:json_annotation/json_annotation.dart'; part 'git.g.dart'; /// Model class for a blob. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class GitBlob { GitBlob({ this.content, @@ -14,11 +13,11 @@ class GitBlob { this.sha, this.size, }); - String content; - String encoding; - String url; - String sha; - int size; + String? content; + String? encoding; + String? url; + String? sha; + int? size; factory GitBlob.fromJson(Map input) => _$GitBlobFromJson(input); @@ -28,12 +27,12 @@ class GitBlob { /// Model class for a new blob to be created. /// /// The [encoding] can be either 'utf-8' or 'base64'. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateGitBlob { CreateGitBlob(this.content, this.encoding); - String content; - String encoding; + String? content; + String? encoding; factory CreateGitBlob.fromJson(Map input) => _$CreateGitBlobFromJson(input); @@ -44,7 +43,7 @@ class CreateGitBlob { /// /// Note: This is the raw [GitCommit]. The [RepositoryCommit] is a repository /// commit containing GitHub-specific information. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class GitCommit { GitCommit({ this.sha, @@ -56,16 +55,16 @@ class GitCommit { this.parents, this.commentCount, }); - String sha; - String url; - GitCommitUser author; - GitCommitUser committer; - String message; - GitTree tree; - List parents; + String? sha; + String? url; + GitCommitUser? author; + GitCommitUser? committer; + String? message; + GitTree? tree; + List? parents; @JsonKey(name: 'comment_count') - int commentCount; + int? commentCount; factory GitCommit.fromJson(Map input) => _$GitCommitFromJson(input); @@ -73,26 +72,26 @@ class GitCommit { } /// Model class for a new commit to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateGitCommit { CreateGitCommit(this.message, this.tree, {this.parents, this.committer, this.author}); /// The commit message. - String message; + String? message; /// The SHA of the tree object this commit points to. - String tree; + String? tree; /// The SHAs of the commits that were the parents of this commit. If omitted /// or empty, the commit will be written as a root commit. - List parents; + List? parents; /// Info about the committer. - GitCommitUser committer; + GitCommitUser? committer; /// Info about the author. - GitCommitUser author; + GitCommitUser? author; factory CreateGitCommit.fromJson(Map input) => _$CreateGitCommitFromJson(input); @@ -105,10 +104,10 @@ class CreateGitCommit { class GitCommitUser { GitCommitUser(this.name, this.email, this.date); - String name; - String email; + String? name; + String? email; @JsonKey(toJson: dateToGitHubIso8601) - DateTime date; + DateTime? date; factory GitCommitUser.fromJson(Map json) => _$GitCommitUserFromJson(json); @@ -117,17 +116,17 @@ class GitCommitUser { } /// Model class for a GitHub tree. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class GitTree { - String sha; - String url; + String? sha; + String? url; /// If truncated is true, the number of items in the tree array exceeded /// GitHub's maximum limit. - bool truncated; + bool? truncated; @JsonKey(name: 'tree') - List entries; + List? entries; GitTree(this.sha, this.url, this.truncated, this.entries); @@ -139,14 +138,14 @@ class GitTree { /// Model class for the contents of a tree structure. [GitTreeEntry] can /// represent either a blog, a commit (in the case of a submodule), or another /// tree. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class GitTreeEntry { - String path; - String mode; - String type; - int size; - String sha; - String url; + String? path; + String? mode; + String? type; + int? size; + String? sha; + String? url; GitTreeEntry(this.path, this.mode, this.type, this.size, this.sha, this.url); @@ -156,7 +155,7 @@ class GitTreeEntry { } /// Model class for a new tree to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateGitTree { CreateGitTree(this.entries, {this.baseTree}); @@ -164,11 +163,11 @@ class CreateGitTree { /// If you don’t set this, the commit will be created on top of everything; /// however, it will only contain your change, the rest of your files will /// show up as deleted. - String baseTree; + String? baseTree; /// The Objects specifying a tree structure. @JsonKey(name: 'tree') - List entries; + List? entries; factory CreateGitTree.fromJson(Map input) => _$CreateGitTreeFromJson(input); @@ -176,7 +175,7 @@ class CreateGitTree { } /// Model class for a new tree entry to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateGitTreeEntry { /// Constructor. /// Either [sha] or [content] must be defined. @@ -187,11 +186,11 @@ class CreateGitTreeEntry { this.sha, this.content, }); - String path; - String mode; - String type; - String sha; - String content; + String? path; + String? mode; + String? type; + String? sha; + String? content; factory CreateGitTreeEntry.fromJson(Map input) => _$CreateGitTreeEntryFromJson(input); @@ -199,16 +198,16 @@ class CreateGitTreeEntry { } /// Model class for a reference. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class GitReference { GitReference({ this.ref, this.url, this.object, }); - String ref; - String url; - GitObject object; + String? ref; + String? url; + GitObject? object; factory GitReference.fromJson(Map input) => _$GitReferenceFromJson(input); @@ -216,7 +215,7 @@ class GitReference { } /// Model class for a tag. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class GitTag { GitTag({ this.tag, @@ -226,12 +225,12 @@ class GitTag { this.tagger, this.object, }); - String tag; - String sha; - String url; - String message; - GitCommitUser tagger; - GitObject object; + String? tag; + String? sha; + String? url; + String? message; + GitCommitUser? tagger; + GitObject? object; factory GitTag.fromJson(Map input) => _$GitTagFromJson(input); @@ -239,15 +238,15 @@ class GitTag { } /// Model class for a new tag to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateGitTag { CreateGitTag(this.tag, this.message, this.object, this.type, this.tagger); - String tag; - String message; - String object; - String type; - GitCommitUser tagger; + String? tag; + String? message; + String? object; + String? type; + GitCommitUser? tagger; factory CreateGitTag.fromJson(Map input) => _$CreateGitTagFromJson(input); @@ -255,12 +254,12 @@ class CreateGitTag { } /// Model class for an object referenced by [GitReference] and [GitTag]. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class GitObject { GitObject(this.type, this.sha, this.url); - String type; - String sha; - String url; + String? type; + String? sha; + String? url; factory GitObject.fromJson(Map input) => _$GitObjectFromJson(input); diff --git a/lib/src/common/model/git.g.dart b/lib/src/common/model/git.g.dart index 64321add..ccbb082b 100644 --- a/lib/src/common/model/git.g.dart +++ b/lib/src/common/model/git.g.dart @@ -6,15 +6,13 @@ part of 'git.dart'; // JsonSerializableGenerator // ************************************************************************** -GitBlob _$GitBlobFromJson(Map json) { - return GitBlob( - content: json['content'] as String, - encoding: json['encoding'] as String, - url: json['url'] as String, - sha: json['sha'] as String, - size: json['size'] as int, - ); -} +GitBlob _$GitBlobFromJson(Map json) => GitBlob( + content: json['content'] as String?, + encoding: json['encoding'] as String?, + url: json['url'] as String?, + sha: json['sha'] as String?, + size: (json['size'] as num?)?.toInt(), + ); Map _$GitBlobToJson(GitBlob instance) => { 'content': instance.content, @@ -24,12 +22,11 @@ Map _$GitBlobToJson(GitBlob instance) => { 'size': instance.size, }; -CreateGitBlob _$CreateGitBlobFromJson(Map json) { - return CreateGitBlob( - json['content'] as String, - json['encoding'] as String, - ); -} +CreateGitBlob _$CreateGitBlobFromJson(Map json) => + CreateGitBlob( + json['content'] as String?, + json['encoding'] as String?, + ); Map _$CreateGitBlobToJson(CreateGitBlob instance) => { @@ -37,27 +34,24 @@ Map _$CreateGitBlobToJson(CreateGitBlob instance) => 'encoding': instance.encoding, }; -GitCommit _$GitCommitFromJson(Map json) { - return GitCommit( - sha: json['sha'] as String, - url: json['url'] as String, - author: json['author'] == null - ? null - : GitCommitUser.fromJson(json['author'] as Map), - committer: json['committer'] == null - ? null - : GitCommitUser.fromJson(json['committer'] as Map), - message: json['message'] as String, - tree: json['tree'] == null - ? null - : GitTree.fromJson(json['tree'] as Map), - parents: (json['parents'] as List) - ?.map((e) => - e == null ? null : GitCommit.fromJson(e as Map)) - ?.toList(), - commentCount: json['comment_count'] as int, - ); -} +GitCommit _$GitCommitFromJson(Map json) => GitCommit( + sha: json['sha'] as String?, + url: json['url'] as String?, + author: json['author'] == null + ? null + : GitCommitUser.fromJson(json['author'] as Map), + committer: json['committer'] == null + ? null + : GitCommitUser.fromJson(json['committer'] as Map), + message: json['message'] as String?, + tree: json['tree'] == null + ? null + : GitTree.fromJson(json['tree'] as Map), + parents: (json['parents'] as List?) + ?.map((e) => GitCommit.fromJson(e as Map)) + .toList(), + commentCount: (json['comment_count'] as num?)?.toInt(), + ); Map _$GitCommitToJson(GitCommit instance) => { 'sha': instance.sha, @@ -70,19 +64,20 @@ Map _$GitCommitToJson(GitCommit instance) => { 'comment_count': instance.commentCount, }; -CreateGitCommit _$CreateGitCommitFromJson(Map json) { - return CreateGitCommit( - json['message'] as String, - json['tree'] as String, - parents: (json['parents'] as List)?.map((e) => e as String)?.toList(), - committer: json['committer'] == null - ? null - : GitCommitUser.fromJson(json['committer'] as Map), - author: json['author'] == null - ? null - : GitCommitUser.fromJson(json['author'] as Map), - ); -} +CreateGitCommit _$CreateGitCommitFromJson(Map json) => + CreateGitCommit( + json['message'] as String?, + json['tree'] as String?, + parents: (json['parents'] as List?) + ?.map((e) => e as String?) + .toList(), + committer: json['committer'] == null + ? null + : GitCommitUser.fromJson(json['committer'] as Map), + author: json['author'] == null + ? null + : GitCommitUser.fromJson(json['author'] as Map), + ); Map _$CreateGitCommitToJson(CreateGitCommit instance) => { @@ -93,40 +88,28 @@ Map _$CreateGitCommitToJson(CreateGitCommit instance) => 'author': instance.author, }; -GitCommitUser _$GitCommitUserFromJson(Map json) { - return GitCommitUser( - json['name'] as String, - json['email'] as String, - json['date'] == null ? null : DateTime.parse(json['date'] as String), - ); -} +GitCommitUser _$GitCommitUserFromJson(Map json) => + GitCommitUser( + json['name'] as String?, + json['email'] as String?, + json['date'] == null ? null : DateTime.parse(json['date'] as String), + ); -Map _$GitCommitUserToJson(GitCommitUser instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('name', instance.name); - writeNotNull('email', instance.email); - writeNotNull('date', dateToGitHubIso8601(instance.date)); - return val; -} +Map _$GitCommitUserToJson(GitCommitUser instance) => + { + if (instance.name case final value?) 'name': value, + if (instance.email case final value?) 'email': value, + if (dateToGitHubIso8601(instance.date) case final value?) 'date': value, + }; -GitTree _$GitTreeFromJson(Map json) { - return GitTree( - json['sha'] as String, - json['url'] as String, - json['truncated'] as bool, - (json['tree'] as List) - ?.map((e) => - e == null ? null : GitTreeEntry.fromJson(e as Map)) - ?.toList(), - ); -} +GitTree _$GitTreeFromJson(Map json) => GitTree( + json['sha'] as String?, + json['url'] as String?, + json['truncated'] as bool?, + (json['tree'] as List?) + ?.map((e) => GitTreeEntry.fromJson(e as Map)) + .toList(), + ); Map _$GitTreeToJson(GitTree instance) => { 'sha': instance.sha, @@ -135,16 +118,14 @@ Map _$GitTreeToJson(GitTree instance) => { 'tree': instance.entries, }; -GitTreeEntry _$GitTreeEntryFromJson(Map json) { - return GitTreeEntry( - json['path'] as String, - json['mode'] as String, - json['type'] as String, - json['size'] as int, - json['sha'] as String, - json['url'] as String, - ); -} +GitTreeEntry _$GitTreeEntryFromJson(Map json) => GitTreeEntry( + json['path'] as String?, + json['mode'] as String?, + json['type'] as String?, + (json['size'] as num?)?.toInt(), + json['sha'] as String?, + json['url'] as String?, + ); Map _$GitTreeEntryToJson(GitTreeEntry instance) => { @@ -156,16 +137,13 @@ Map _$GitTreeEntryToJson(GitTreeEntry instance) => 'url': instance.url, }; -CreateGitTree _$CreateGitTreeFromJson(Map json) { - return CreateGitTree( - (json['tree'] as List) - ?.map((e) => e == null - ? null - : CreateGitTreeEntry.fromJson(e as Map)) - ?.toList(), - baseTree: json['base_tree'] as String, - ); -} +CreateGitTree _$CreateGitTreeFromJson(Map json) => + CreateGitTree( + (json['tree'] as List?) + ?.map((e) => CreateGitTreeEntry.fromJson(e as Map)) + .toList(), + baseTree: json['base_tree'] as String?, + ); Map _$CreateGitTreeToJson(CreateGitTree instance) => { @@ -173,15 +151,14 @@ Map _$CreateGitTreeToJson(CreateGitTree instance) => 'tree': instance.entries, }; -CreateGitTreeEntry _$CreateGitTreeEntryFromJson(Map json) { - return CreateGitTreeEntry( - json['path'] as String, - json['mode'] as String, - json['type'] as String, - sha: json['sha'] as String, - content: json['content'] as String, - ); -} +CreateGitTreeEntry _$CreateGitTreeEntryFromJson(Map json) => + CreateGitTreeEntry( + json['path'] as String?, + json['mode'] as String?, + json['type'] as String?, + sha: json['sha'] as String?, + content: json['content'] as String?, + ); Map _$CreateGitTreeEntryToJson(CreateGitTreeEntry instance) => { @@ -192,15 +169,13 @@ Map _$CreateGitTreeEntryToJson(CreateGitTreeEntry instance) => 'content': instance.content, }; -GitReference _$GitReferenceFromJson(Map json) { - return GitReference( - ref: json['ref'] as String, - url: json['url'] as String, - object: json['object'] == null - ? null - : GitObject.fromJson(json['object'] as Map), - ); -} +GitReference _$GitReferenceFromJson(Map json) => GitReference( + ref: json['ref'] as String?, + url: json['url'] as String?, + object: json['object'] == null + ? null + : GitObject.fromJson(json['object'] as Map), + ); Map _$GitReferenceToJson(GitReference instance) => { @@ -209,20 +184,18 @@ Map _$GitReferenceToJson(GitReference instance) => 'object': instance.object, }; -GitTag _$GitTagFromJson(Map json) { - return GitTag( - tag: json['tag'] as String, - sha: json['sha'] as String, - url: json['url'] as String, - message: json['message'] as String, - tagger: json['tagger'] == null - ? null - : GitCommitUser.fromJson(json['tagger'] as Map), - object: json['object'] == null - ? null - : GitObject.fromJson(json['object'] as Map), - ); -} +GitTag _$GitTagFromJson(Map json) => GitTag( + tag: json['tag'] as String?, + sha: json['sha'] as String?, + url: json['url'] as String?, + message: json['message'] as String?, + tagger: json['tagger'] == null + ? null + : GitCommitUser.fromJson(json['tagger'] as Map), + object: json['object'] == null + ? null + : GitObject.fromJson(json['object'] as Map), + ); Map _$GitTagToJson(GitTag instance) => { 'tag': instance.tag, @@ -233,17 +206,15 @@ Map _$GitTagToJson(GitTag instance) => { 'object': instance.object, }; -CreateGitTag _$CreateGitTagFromJson(Map json) { - return CreateGitTag( - json['tag'] as String, - json['message'] as String, - json['object'] as String, - json['type'] as String, - json['tagger'] == null - ? null - : GitCommitUser.fromJson(json['tagger'] as Map), - ); -} +CreateGitTag _$CreateGitTagFromJson(Map json) => CreateGitTag( + json['tag'] as String?, + json['message'] as String?, + json['object'] as String?, + json['type'] as String?, + json['tagger'] == null + ? null + : GitCommitUser.fromJson(json['tagger'] as Map), + ); Map _$CreateGitTagToJson(CreateGitTag instance) => { @@ -254,13 +225,11 @@ Map _$CreateGitTagToJson(CreateGitTag instance) => 'tagger': instance.tagger, }; -GitObject _$GitObjectFromJson(Map json) { - return GitObject( - json['type'] as String, - json['sha'] as String, - json['url'] as String, - ); -} +GitObject _$GitObjectFromJson(Map json) => GitObject( + json['type'] as String?, + json['sha'] as String?, + json['url'] as String?, + ); Map _$GitObjectToJson(GitObject instance) => { 'type': instance.type, diff --git a/lib/src/common/model/issues.dart b/lib/src/common/model/issues.dart index 254881b4..3d531e63 100644 --- a/lib/src/common/model/issues.dart +++ b/lib/src/common/model/issues.dart @@ -1,31 +1,53 @@ import 'package:github/src/common.dart'; -import 'package:github/src/common/model/users.dart'; import 'package:json_annotation/json_annotation.dart'; part 'issues.g.dart'; /// Model class for an issue on the tracker. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class Issue { Issue({ - this.id, - this.url, - this.htmlUrl, - this.number, - this.state, - this.title, + this.id = 0, + this.url = '', + this.htmlUrl = '', + this.number = 0, + this.state = '', + this.title = '', this.user, - this.labels, + List? labels, this.assignee, + this.assignees, this.milestone, - this.commentsCount, + this.commentsCount = 0, this.pullRequest, this.createdAt, this.closedAt, this.updatedAt, - this.body, + this.body = '', this.closedBy, - }); + + // Properties from the Timeline API + this.activeLockReason, + this.authorAssociation, + this.bodyHtml, + this.bodyText, + this.commentsUrl, + this.draft, + this.eventsUrl, + this.labelsUrl, + this.locked, + this.nodeId, + this.performedViaGithubApp, + this.reactions, + this.repository, + this.repositoryUrl, + this.stateReason, + this.timelineUrl, + }) { + if (labels != null) { + this.labels = labels; + } + } int id; @@ -33,7 +55,6 @@ class Issue { String url; /// Url to the Issue Page - @JsonKey(name: 'html_url') String htmlUrl; /// Issue Number @@ -46,66 +67,107 @@ class Issue { String title; /// User who created the issue. - User user; + User? user; /// Issue Labels - List labels; + @JsonKey(defaultValue: []) + List labels = []; + + /// The User that the issue is assigned to + User? assignee; /// The User that the issue is assigned to - User assignee; + List? assignees; /// The Milestone - Milestone milestone; + Milestone? milestone; /// Number of Comments @JsonKey(name: 'comments') int commentsCount; /// A Pull Request - @JsonKey(name: 'pull_request') - IssuePullRequest pullRequest; + IssuePullRequest? pullRequest; /// Time that the issue was created at - @JsonKey(name: 'created_at') - DateTime createdAt; + DateTime? createdAt; /// The time that the issue was closed at - @JsonKey(name: 'closed_at') - DateTime closedAt; + DateTime? closedAt; /// The time that the issue was updated at - @JsonKey(name: 'updated_at') - DateTime updatedAt; + DateTime? updatedAt; String body; /// The user who closed the issue - @JsonKey(name: 'closed_by') - User closedBy; + User? closedBy; + + bool get isOpen => state.toUpperCase() == 'OPEN'; + bool get isClosed => state.toUpperCase() == 'CLOSED'; + + // The following properties were added to support the Timeline API. + + String? activeLockReason; + + /// How the author is associated with the repository. + /// + /// Example: `OWNER` + String? authorAssociation; - bool get isOpen => state == 'open'; - bool get isClosed => state == 'closed'; + String? bodyHtml; + + String? bodyText; + + String? commentsUrl; + + bool? draft; + + String? eventsUrl; + + String? labelsUrl; + + bool? locked; + + String? nodeId; + + GitHubApp? performedViaGithubApp; + + ReactionRollup? reactions; + + Repository? repository; + + String? repositoryUrl; + + /// The reason for the current state + /// + /// Example: `not_planned` + String? stateReason; + + String? timelineUrl; factory Issue.fromJson(Map input) => _$IssueFromJson(input); Map toJson() => _$IssueToJson(this); } /// Model class for a request to create/edit an issue. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class IssueRequest { IssueRequest( {this.title, this.body, this.labels, this.assignee, + this.assignees, this.state, this.milestone}); - String title; - String body; - List labels; - String assignee; - String state; - int milestone; + String? title; + String? body; + List? labels; + String? assignee; + List? assignees; + String? state; + int? milestone; Map toJson() => _$IssueRequestToJson(this); @@ -114,7 +176,7 @@ class IssueRequest { } /// Model class for a pull request for an issue. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class IssuePullRequest { IssuePullRequest({ this.htmlUrl, @@ -123,9 +185,9 @@ class IssuePullRequest { }); /// Url to the Page for this Issue Pull Request - String htmlUrl; - String diffUrl; - String patchUrl; + String? htmlUrl; + String? diffUrl; + String? patchUrl; factory IssuePullRequest.fromJson(Map input) => _$IssuePullRequestFromJson(input); @@ -133,7 +195,7 @@ class IssuePullRequest { } /// Model class for an issue comment. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class IssueComment { IssueComment({ this.id, @@ -144,15 +206,17 @@ class IssueComment { this.url, this.htmlUrl, this.issueUrl, + this.authorAssociation, }); - int id; - String body; - User user; - DateTime createdAt; - DateTime updatedAt; - String url; - String htmlUrl; - String issueUrl; + int? id; + String? body; + User? user; + DateTime? createdAt; + DateTime? updatedAt; + String? url; + String? htmlUrl; + String? issueUrl; + String? authorAssociation; factory IssueComment.fromJson(Map input) => _$IssueCommentFromJson(input); @@ -160,16 +224,20 @@ class IssueComment { } /// Model class for an issue label. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class IssueLabel { IssueLabel({ - this.name, - this.color, + this.name = '', + this.color = '', + this.description = '', }); String name; + String color; + String description; + factory IssueLabel.fromJson(Map input) => _$IssueLabelFromJson(input); Map toJson() => _$IssueLabelToJson(this); @@ -179,7 +247,7 @@ class IssueLabel { } /// Model class for a milestone. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class Milestone { Milestone({ this.id, @@ -193,42 +261,65 @@ class Milestone { this.createdAt, this.updatedAt, this.dueOn, + + // Properties from the Timeline API + this.closedAt, + this.htmlUrl, + this.labelsUrl, + this.nodeId, + this.url, }); /// Unique Identifier for Milestone - int id; + int? id; /// Milestone Number - int number; + int? number; /// Milestone State - String state; + String? state; /// Milestone Title - String title; + String? title; /// Milestone Description - String description; + String? description; /// Milestone Creator - User creator; + User? creator; /// Number of Open Issues @JsonKey(name: 'open_issues') - int openIssuesCount; + int? openIssuesCount; /// Number of Closed Issues @JsonKey(name: 'closed_issues') - int closedIssuesCount; + int? closedIssuesCount; /// Time the milestone was created at - DateTime createdAt; + DateTime? createdAt; /// The last time the milestone was updated at - DateTime updatedAt; + DateTime? updatedAt; /// The due date for this milestone - DateTime dueOn; + DateTime? dueOn; + + // The following properties were added to support the Timeline API. + + DateTime? closedAt; + + /// Example: `https://github.com/octocat/Hello-World/milestones/v1.0` + String? htmlUrl; + + /// Example: `https://api.github.com/repos/octocat/Hello-World/milestones/1/labels` + String? labelsUrl; + + /// Example: `MDk6TWlsZXN0b25lMTAwMjYwNA==` + String? nodeId; + + /// Example: `https://api.github.com/repos/octocat/Hello-World/milestones/1` + String? url; factory Milestone.fromJson(Map input) => _$MilestoneFromJson(input); @@ -236,7 +327,7 @@ class Milestone { } /// Model class for a new milestone to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateMilestone { CreateMilestone( this.title, { @@ -245,10 +336,10 @@ class CreateMilestone { this.dueOn, }); - String title; - String state; - String description; - DateTime dueOn; + String? title; + String? state; + String? description; + DateTime? dueOn; Map toJson() => _$CreateMilestoneToJson(this); diff --git a/lib/src/common/model/issues.g.dart b/lib/src/common/model/issues.g.dart index 975ade86..e4a80082 100644 --- a/lib/src/common/model/issues.g.dart +++ b/lib/src/common/model/issues.g.dart @@ -6,47 +6,71 @@ part of 'issues.dart'; // JsonSerializableGenerator // ************************************************************************** -Issue _$IssueFromJson(Map json) { - return Issue( - id: json['id'] as int, - url: json['url'] as String, - htmlUrl: json['html_url'] as String, - number: json['number'] as int, - state: json['state'] as String, - title: json['title'] as String, - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - labels: (json['labels'] as List) - ?.map((e) => - e == null ? null : IssueLabel.fromJson(e as Map)) - ?.toList(), - assignee: json['assignee'] == null - ? null - : User.fromJson(json['assignee'] as Map), - milestone: json['milestone'] == null - ? null - : Milestone.fromJson(json['milestone'] as Map), - commentsCount: json['comments'] as int, - pullRequest: json['pull_request'] == null - ? null - : IssuePullRequest.fromJson( - json['pull_request'] as Map), - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - closedAt: json['closed_at'] == null - ? null - : DateTime.parse(json['closed_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - body: json['body'] as String, - closedBy: json['closed_by'] == null - ? null - : User.fromJson(json['closed_by'] as Map), - ); -} +Issue _$IssueFromJson(Map json) => Issue( + id: (json['id'] as num?)?.toInt() ?? 0, + url: json['url'] as String? ?? '', + htmlUrl: json['html_url'] as String? ?? '', + number: (json['number'] as num?)?.toInt() ?? 0, + state: json['state'] as String? ?? '', + title: json['title'] as String? ?? '', + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + labels: (json['labels'] as List?) + ?.map((e) => IssueLabel.fromJson(e as Map)) + .toList() ?? + [], + assignee: json['assignee'] == null + ? null + : User.fromJson(json['assignee'] as Map), + assignees: (json['assignees'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList(), + milestone: json['milestone'] == null + ? null + : Milestone.fromJson(json['milestone'] as Map), + commentsCount: (json['comments'] as num?)?.toInt() ?? 0, + pullRequest: json['pull_request'] == null + ? null + : IssuePullRequest.fromJson( + json['pull_request'] as Map), + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + closedAt: json['closed_at'] == null + ? null + : DateTime.parse(json['closed_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + body: json['body'] as String? ?? '', + closedBy: json['closed_by'] == null + ? null + : User.fromJson(json['closed_by'] as Map), + activeLockReason: json['active_lock_reason'] as String?, + authorAssociation: json['author_association'] as String?, + bodyHtml: json['body_html'] as String?, + bodyText: json['body_text'] as String?, + commentsUrl: json['comments_url'] as String?, + draft: json['draft'] as bool?, + eventsUrl: json['events_url'] as String?, + labelsUrl: json['labels_url'] as String?, + locked: json['locked'] as bool?, + nodeId: json['node_id'] as String?, + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + reactions: json['reactions'] == null + ? null + : ReactionRollup.fromJson(json['reactions'] as Map), + repository: json['repository'] == null + ? null + : Repository.fromJson(json['repository'] as Map), + repositoryUrl: json['repository_url'] as String?, + stateReason: json['state_reason'] as String?, + timelineUrl: json['timeline_url'] as String?, + ); Map _$IssueToJson(Issue instance) => { 'id': instance.id, @@ -58,6 +82,7 @@ Map _$IssueToJson(Issue instance) => { 'user': instance.user, 'labels': instance.labels, 'assignee': instance.assignee, + 'assignees': instance.assignees, 'milestone': instance.milestone, 'comments': instance.commentsCount, 'pull_request': instance.pullRequest, @@ -66,18 +91,36 @@ Map _$IssueToJson(Issue instance) => { 'updated_at': instance.updatedAt?.toIso8601String(), 'body': instance.body, 'closed_by': instance.closedBy, + 'active_lock_reason': instance.activeLockReason, + 'author_association': instance.authorAssociation, + 'body_html': instance.bodyHtml, + 'body_text': instance.bodyText, + 'comments_url': instance.commentsUrl, + 'draft': instance.draft, + 'events_url': instance.eventsUrl, + 'labels_url': instance.labelsUrl, + 'locked': instance.locked, + 'node_id': instance.nodeId, + 'performed_via_github_app': instance.performedViaGithubApp, + 'reactions': instance.reactions, + 'repository': instance.repository, + 'repository_url': instance.repositoryUrl, + 'state_reason': instance.stateReason, + 'timeline_url': instance.timelineUrl, }; -IssueRequest _$IssueRequestFromJson(Map json) { - return IssueRequest( - title: json['title'] as String, - body: json['body'] as String, - labels: (json['labels'] as List)?.map((e) => e as String)?.toList(), - assignee: json['assignee'] as String, - state: json['state'] as String, - milestone: json['milestone'] as int, - ); -} +IssueRequest _$IssueRequestFromJson(Map json) => IssueRequest( + title: json['title'] as String?, + body: json['body'] as String?, + labels: + (json['labels'] as List?)?.map((e) => e as String).toList(), + assignee: json['assignee'] as String?, + assignees: (json['assignees'] as List?) + ?.map((e) => e as String) + .toList(), + state: json['state'] as String?, + milestone: (json['milestone'] as num?)?.toInt(), + ); Map _$IssueRequestToJson(IssueRequest instance) => { @@ -85,17 +128,17 @@ Map _$IssueRequestToJson(IssueRequest instance) => 'body': instance.body, 'labels': instance.labels, 'assignee': instance.assignee, + 'assignees': instance.assignees, 'state': instance.state, 'milestone': instance.milestone, }; -IssuePullRequest _$IssuePullRequestFromJson(Map json) { - return IssuePullRequest( - htmlUrl: json['html_url'] as String, - diffUrl: json['diff_url'] as String, - patchUrl: json['patch_url'] as String, - ); -} +IssuePullRequest _$IssuePullRequestFromJson(Map json) => + IssuePullRequest( + htmlUrl: json['html_url'] as String?, + diffUrl: json['diff_url'] as String?, + patchUrl: json['patch_url'] as String?, + ); Map _$IssuePullRequestToJson(IssuePullRequest instance) => { @@ -104,24 +147,23 @@ Map _$IssuePullRequestToJson(IssuePullRequest instance) => 'patch_url': instance.patchUrl, }; -IssueComment _$IssueCommentFromJson(Map json) { - return IssueComment( - id: json['id'] as int, - body: json['body'] as String, - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - url: json['url'] as String, - htmlUrl: json['html_url'] as String, - issueUrl: json['issue_url'] as String, - ); -} +IssueComment _$IssueCommentFromJson(Map json) => IssueComment( + id: (json['id'] as num?)?.toInt(), + body: json['body'] as String?, + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + url: json['url'] as String?, + htmlUrl: json['html_url'] as String?, + issueUrl: json['issue_url'] as String?, + authorAssociation: json['author_association'] as String?, + ); Map _$IssueCommentToJson(IssueComment instance) => { @@ -133,44 +175,50 @@ Map _$IssueCommentToJson(IssueComment instance) => 'url': instance.url, 'html_url': instance.htmlUrl, 'issue_url': instance.issueUrl, + 'author_association': instance.authorAssociation, }; -IssueLabel _$IssueLabelFromJson(Map json) { - return IssueLabel( - name: json['name'] as String, - color: json['color'] as String, - ); -} +IssueLabel _$IssueLabelFromJson(Map json) => IssueLabel( + name: json['name'] as String? ?? '', + color: json['color'] as String? ?? '', + description: json['description'] as String? ?? '', + ); Map _$IssueLabelToJson(IssueLabel instance) => { 'name': instance.name, 'color': instance.color, + 'description': instance.description, }; -Milestone _$MilestoneFromJson(Map json) { - return Milestone( - id: json['id'] as int, - number: json['number'] as int, - state: json['state'] as String, - title: json['title'] as String, - description: json['description'] as String, - creator: json['creator'] == null - ? null - : User.fromJson(json['creator'] as Map), - openIssuesCount: json['open_issues'] as int, - closedIssuesCount: json['closed_issues'] as int, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - dueOn: json['due_on'] == null - ? null - : DateTime.parse(json['due_on'] as String), - ); -} +Milestone _$MilestoneFromJson(Map json) => Milestone( + id: (json['id'] as num?)?.toInt(), + number: (json['number'] as num?)?.toInt(), + state: json['state'] as String?, + title: json['title'] as String?, + description: json['description'] as String?, + creator: json['creator'] == null + ? null + : User.fromJson(json['creator'] as Map), + openIssuesCount: (json['open_issues'] as num?)?.toInt(), + closedIssuesCount: (json['closed_issues'] as num?)?.toInt(), + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + dueOn: json['due_on'] == null + ? null + : DateTime.parse(json['due_on'] as String), + closedAt: json['closed_at'] == null + ? null + : DateTime.parse(json['closed_at'] as String), + htmlUrl: json['html_url'] as String?, + labelsUrl: json['labels_url'] as String?, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + ); Map _$MilestoneToJson(Milestone instance) => { 'id': instance.id, @@ -184,18 +232,22 @@ Map _$MilestoneToJson(Milestone instance) => { 'created_at': instance.createdAt?.toIso8601String(), 'updated_at': instance.updatedAt?.toIso8601String(), 'due_on': instance.dueOn?.toIso8601String(), + 'closed_at': instance.closedAt?.toIso8601String(), + 'html_url': instance.htmlUrl, + 'labels_url': instance.labelsUrl, + 'node_id': instance.nodeId, + 'url': instance.url, }; -CreateMilestone _$CreateMilestoneFromJson(Map json) { - return CreateMilestone( - json['title'] as String, - state: json['state'] as String, - description: json['description'] as String, - dueOn: json['due_on'] == null - ? null - : DateTime.parse(json['due_on'] as String), - ); -} +CreateMilestone _$CreateMilestoneFromJson(Map json) => + CreateMilestone( + json['title'] as String?, + state: json['state'] as String?, + description: json['description'] as String?, + dueOn: json['due_on'] == null + ? null + : DateTime.parse(json['due_on'] as String), + ); Map _$CreateMilestoneToJson(CreateMilestone instance) => { diff --git a/lib/src/common/model/keys.dart b/lib/src/common/model/keys.dart index c59c226b..1628a376 100644 --- a/lib/src/common/model/keys.dart +++ b/lib/src/common/model/keys.dart @@ -6,16 +6,16 @@ part 'keys.g.dart'; /// /// Note: [PublicKey] is used both by the repositories' deploy keys and by the /// users' public keys. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class PublicKey { PublicKey({ this.id, this.key, this.title, }); - final int id; - final String key; - final String title; + final int? id; + final String? key; + final String? title; factory PublicKey.fromJson(Map input) => _$PublicKeyFromJson(input); @@ -23,12 +23,12 @@ class PublicKey { } /// Model class for a new public key to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreatePublicKey { CreatePublicKey(this.title, this.key); - final String title; - final String key; + final String? title; + final String? key; Map toJson() => _$CreatePublicKeyToJson(this); diff --git a/lib/src/common/model/keys.g.dart b/lib/src/common/model/keys.g.dart index b99277c8..7cecbcf2 100644 --- a/lib/src/common/model/keys.g.dart +++ b/lib/src/common/model/keys.g.dart @@ -6,13 +6,11 @@ part of 'keys.dart'; // JsonSerializableGenerator // ************************************************************************** -PublicKey _$PublicKeyFromJson(Map json) { - return PublicKey( - id: json['id'] as int, - key: json['key'] as String, - title: json['title'] as String, - ); -} +PublicKey _$PublicKeyFromJson(Map json) => PublicKey( + id: (json['id'] as num?)?.toInt(), + key: json['key'] as String?, + title: json['title'] as String?, + ); Map _$PublicKeyToJson(PublicKey instance) => { 'id': instance.id, @@ -20,12 +18,11 @@ Map _$PublicKeyToJson(PublicKey instance) => { 'title': instance.title, }; -CreatePublicKey _$CreatePublicKeyFromJson(Map json) { - return CreatePublicKey( - json['title'] as String, - json['key'] as String, - ); -} +CreatePublicKey _$CreatePublicKeyFromJson(Map json) => + CreatePublicKey( + json['title'] as String?, + json['key'] as String?, + ); Map _$CreatePublicKeyToJson(CreatePublicKey instance) => { diff --git a/lib/src/common/model/misc.dart b/lib/src/common/model/misc.dart index b8e6ee77..18fc0a54 100644 --- a/lib/src/common/model/misc.dart +++ b/lib/src/common/model/misc.dart @@ -3,39 +3,55 @@ import 'package:json_annotation/json_annotation.dart'; part 'misc.g.dart'; /// Model class for a Gitignore Template. -@JsonSerializable(createToJson: false) +@JsonSerializable() class GitignoreTemplate { GitignoreTemplate({this.name, this.source}); /// Template Name - final String name; + final String? name; /// Template Source - final String source; + final String? source; factory GitignoreTemplate.fromJson(Map input) => _$GitignoreTemplateFromJson(input); + Map toJson() => _$GitignoreTemplateToJson(this); } /// Model class for GitHub Rate Limit Information. @JsonSerializable() class RateLimit { /// Maximum number of requests - final int limit; + final int? limit; /// Remaining number of requests - final int remaining; + final int? remaining; /// Time when the limit expires - final DateTime resets; + final DateTime? resets; RateLimit(this.limit, this.remaining, this.resets); factory RateLimit.fromHeaders(Map headers) { - final limit = int.parse(headers['x-ratelimit-limit']); - final remaining = int.parse(headers['x-ratelimit-remaining']); + final limit = int.parse(headers['x-ratelimit-limit']!); + final remaining = int.parse(headers['x-ratelimit-remaining']!); final resets = DateTime.fromMillisecondsSinceEpoch( - int.parse(headers['x-ratelimit-reset']) * 1000); + int.parse(headers['x-ratelimit-reset']!) * 1000); + return RateLimit(limit, remaining, resets); + } + + /// Construct [RateLimit] from JSON response of /rate_limit. + /// + /// API docs: https://developer.github.com/v3/rate_limit/ + factory RateLimit.fromRateLimitResponse(Map response) { + final rateJson = response['rate'] == null + ? null + : response['rate'] as Map; + final limit = rateJson?['limit'] as int?; + final remaining = rateJson?['remaining'] as int?; + final resets = rateJson?['reset'] == null + ? null + : DateTime.fromMillisecondsSinceEpoch(rateJson?['reset']); return RateLimit(limit, remaining, resets); } @@ -44,27 +60,65 @@ class RateLimit { Map toJson() => _$RateLimitToJson(this); } -/// Model class for the GitHub api status. +/// Model class for the GitHub API status. @JsonSerializable() class APIStatus { APIStatus({ + this.page, this.status, - this.lastUpdatedAt, - this.createdOn, - this.message, }); - final String status; - - @JsonKey(name: 'last_updated') - final DateTime lastUpdatedAt; - @JsonKey(name: 'created_on') - final DateTime createdOn; + /// Details about where to find more information. + final APIStatusPage? page; - @JsonKey(name: 'body') - final String message; + /// An overview of the current status. + final APIStatusMessage? status; factory APIStatus.fromJson(Map input) => _$APIStatusFromJson(input); Map toJson() => _$APIStatusToJson(this); } + +@JsonSerializable() +class APIStatusPage { + const APIStatusPage({ + this.id, + this.name, + this.url, + this.updatedAt, + }); + + /// Unique identifier for the current status. + final String? id; + + final String? name; + + /// Where to get more detailed information. + final String? url; + + @JsonKey(name: 'updated_at') + final DateTime? updatedAt; + + factory APIStatusPage.fromJson(Map input) => + _$APIStatusPageFromJson(input); + Map toJson() => _$APIStatusPageToJson(this); +} + +/// Overview class of the GitHub API status. +@JsonSerializable() +class APIStatusMessage { + const APIStatusMessage({ + this.description, + this.indicator, + }); + + /// A human description of the blended component status. + final String? description; + + /// An indicator - one of none, minor, major, or critical. + final String? indicator; + + factory APIStatusMessage.fromJson(Map input) => + _$APIStatusMessageFromJson(input); + Map toJson() => _$APIStatusMessageToJson(this); +} diff --git a/lib/src/common/model/misc.g.dart b/lib/src/common/model/misc.g.dart index 24b79965..4ad9a310 100644 --- a/lib/src/common/model/misc.g.dart +++ b/lib/src/common/model/misc.g.dart @@ -6,20 +6,23 @@ part of 'misc.dart'; // JsonSerializableGenerator // ************************************************************************** -GitignoreTemplate _$GitignoreTemplateFromJson(Map json) { - return GitignoreTemplate( - name: json['name'] as String, - source: json['source'] as String, - ); -} - -RateLimit _$RateLimitFromJson(Map json) { - return RateLimit( - json['limit'] as int, - json['remaining'] as int, - json['resets'] == null ? null : DateTime.parse(json['resets'] as String), - ); -} +GitignoreTemplate _$GitignoreTemplateFromJson(Map json) => + GitignoreTemplate( + name: json['name'] as String?, + source: json['source'] as String?, + ); + +Map _$GitignoreTemplateToJson(GitignoreTemplate instance) => + { + 'name': instance.name, + 'source': instance.source, + }; + +RateLimit _$RateLimitFromJson(Map json) => RateLimit( + (json['limit'] as num?)?.toInt(), + (json['remaining'] as num?)?.toInt(), + json['resets'] == null ? null : DateTime.parse(json['resets'] as String), + ); Map _$RateLimitToJson(RateLimit instance) => { 'limit': instance.limit, @@ -27,22 +30,46 @@ Map _$RateLimitToJson(RateLimit instance) => { 'resets': instance.resets?.toIso8601String(), }; -APIStatus _$APIStatusFromJson(Map json) { - return APIStatus( - status: json['status'] as String, - lastUpdatedAt: json['last_updated'] == null - ? null - : DateTime.parse(json['last_updated'] as String), - createdOn: json['created_on'] == null - ? null - : DateTime.parse(json['created_on'] as String), - message: json['body'] as String, - ); -} +APIStatus _$APIStatusFromJson(Map json) => APIStatus( + page: json['page'] == null + ? null + : APIStatusPage.fromJson(json['page'] as Map), + status: json['status'] == null + ? null + : APIStatusMessage.fromJson(json['status'] as Map), + ); Map _$APIStatusToJson(APIStatus instance) => { + 'page': instance.page, 'status': instance.status, - 'last_updated': instance.lastUpdatedAt?.toIso8601String(), - 'created_on': instance.createdOn?.toIso8601String(), - 'body': instance.message, + }; + +APIStatusPage _$APIStatusPageFromJson(Map json) => + APIStatusPage( + id: json['id'] as String?, + name: json['name'] as String?, + url: json['url'] as String?, + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + ); + +Map _$APIStatusPageToJson(APIStatusPage instance) => + { + 'id': instance.id, + 'name': instance.name, + 'url': instance.url, + 'updated_at': instance.updatedAt?.toIso8601String(), + }; + +APIStatusMessage _$APIStatusMessageFromJson(Map json) => + APIStatusMessage( + description: json['description'] as String?, + indicator: json['indicator'] as String?, + ); + +Map _$APIStatusMessageToJson(APIStatusMessage instance) => + { + 'description': instance.description, + 'indicator': instance.indicator, }; diff --git a/lib/src/common/model/notifications.dart b/lib/src/common/model/notifications.dart index 85ae6af0..0fd0477a 100644 --- a/lib/src/common/model/notifications.dart +++ b/lib/src/common/model/notifications.dart @@ -4,7 +4,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'notifications.g.dart'; /// Model class for notifications. -@JsonSerializable(createToJson: false) +@JsonSerializable() class Notification { Notification({ this.id, @@ -17,38 +17,40 @@ class Notification { this.url, this.subscriptionUrl, }); - final String id; - final Repository repository; - final NotificationSubject subject; - final String reason; - final bool unread; + final String? id; + final Repository? repository; + final NotificationSubject? subject; + final String? reason; + final bool? unread; @JsonKey(name: 'updated_at') - final DateTime updatedAt; + final DateTime? updatedAt; @JsonKey(name: 'last_read_at') - final DateTime lastReadAt; + final DateTime? lastReadAt; - final String url; + final String? url; @JsonKey(name: 'subscription_url') - final String subscriptionUrl; + final String? subscriptionUrl; factory Notification.fromJson(Map input) => _$NotificationFromJson(input); + Map toJson() => _$NotificationToJson(this); } /// Model class for a notification subject. -@JsonSerializable(createToJson: false) +@JsonSerializable() class NotificationSubject { NotificationSubject({this.title, this.type, this.url, this.latestCommentUrl}); - final String title; - final String type; - final String url; + final String? title; + final String? type; + final String? url; @JsonKey(name: 'latest_comment_url') - final String latestCommentUrl; + final String? latestCommentUrl; factory NotificationSubject.fromJson(Map input) => _$NotificationSubjectFromJson(input); + Map toJson() => _$NotificationSubjectToJson(this); } diff --git a/lib/src/common/model/notifications.g.dart b/lib/src/common/model/notifications.g.dart index b6fbf6fd..70dfde07 100644 --- a/lib/src/common/model/notifications.g.dart +++ b/lib/src/common/model/notifications.g.dart @@ -6,33 +6,53 @@ part of 'notifications.dart'; // JsonSerializableGenerator // ************************************************************************** -Notification _$NotificationFromJson(Map json) { - return Notification( - id: json['id'] as String, - repository: json['repository'] == null - ? null - : Repository.fromJson(json['repository'] as Map), - subject: json['subject'] == null - ? null - : NotificationSubject.fromJson(json['subject'] as Map), - reason: json['reason'] as String, - unread: json['unread'] as bool, - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - lastReadAt: json['last_read_at'] == null - ? null - : DateTime.parse(json['last_read_at'] as String), - url: json['url'] as String, - subscriptionUrl: json['subscription_url'] as String, - ); -} +Notification _$NotificationFromJson(Map json) => Notification( + id: json['id'] as String?, + repository: json['repository'] == null + ? null + : Repository.fromJson(json['repository'] as Map), + subject: json['subject'] == null + ? null + : NotificationSubject.fromJson( + json['subject'] as Map), + reason: json['reason'] as String?, + unread: json['unread'] as bool?, + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + lastReadAt: json['last_read_at'] == null + ? null + : DateTime.parse(json['last_read_at'] as String), + url: json['url'] as String?, + subscriptionUrl: json['subscription_url'] as String?, + ); -NotificationSubject _$NotificationSubjectFromJson(Map json) { - return NotificationSubject( - title: json['title'] as String, - type: json['type'] as String, - url: json['url'] as String, - latestCommentUrl: json['latest_comment_url'] as String, - ); -} +Map _$NotificationToJson(Notification instance) => + { + 'id': instance.id, + 'repository': instance.repository, + 'subject': instance.subject, + 'reason': instance.reason, + 'unread': instance.unread, + 'updated_at': instance.updatedAt?.toIso8601String(), + 'last_read_at': instance.lastReadAt?.toIso8601String(), + 'url': instance.url, + 'subscription_url': instance.subscriptionUrl, + }; + +NotificationSubject _$NotificationSubjectFromJson(Map json) => + NotificationSubject( + title: json['title'] as String?, + type: json['type'] as String?, + url: json['url'] as String?, + latestCommentUrl: json['latest_comment_url'] as String?, + ); + +Map _$NotificationSubjectToJson( + NotificationSubject instance) => + { + 'title': instance.title, + 'type': instance.type, + 'url': instance.url, + 'latest_comment_url': instance.latestCommentUrl, + }; diff --git a/lib/src/common/model/orgs.dart b/lib/src/common/model/orgs.dart index 386404b9..5a26bb2c 100644 --- a/lib/src/common/model/orgs.dart +++ b/lib/src/common/model/orgs.dart @@ -1,4 +1,3 @@ -import 'package:github/src/common.dart'; import 'package:json_annotation/json_annotation.dart'; part 'orgs.g.dart'; @@ -25,57 +24,57 @@ class Organization { }); /// Organization Login - final String login; + String? login; /// Organization ID - final int id; + int? id; /// Url to Organization Profile @JsonKey(name: 'html_url') - final String htmlUrl; + String? htmlUrl; /// Url to the Organization Avatar @JsonKey(name: 'avatar_url') - final String avatarUrl; + String? avatarUrl; /// Organization Name - final String name; + String? name; /// Organization Company - final String company; + String? company; /// Organization Blog - final String blog; + String? blog; /// Organization Location - final String location; + String? location; /// Organization Email - final String email; + String? email; /// Number of Public Repositories @JsonKey(name: 'public_repos') - final int publicReposCount; + int? publicReposCount; /// Number of Public Gists @JsonKey(name: 'public_gists') - final int publicGistsCount; + int? publicGistsCount; /// Number of Followers @JsonKey(name: 'followers') - final int followersCount; + int? followersCount; /// Number of People this Organization is Following @JsonKey(name: 'following') - final int followingCount; + int? followingCount; /// Time this organization was created @JsonKey(name: 'created_at') - final DateTime createdAt; + DateTime? createdAt; /// Time this organization was updated @JsonKey(name: 'updated_at') - final DateTime updatedAt; + DateTime? updatedAt; factory Organization.fromJson(Map input) => _$OrganizationFromJson(input); @@ -83,70 +82,163 @@ class Organization { } /// Model class for organization membership. -@JsonSerializable(createToJson: false) +@JsonSerializable() class OrganizationMembership { OrganizationMembership({ this.state, this.organization, }); - final String state; - final Organization organization; + + String? state; + Organization? organization; factory OrganizationMembership.fromJson(Map input) { return _$OrganizationMembershipFromJson(input); } + Map toJson() => _$OrganizationMembershipToJson(this); } /// Model class for a GitHub team. -@JsonSerializable(createToJson: false) +/// +/// Different end-points populate different sets of properties. +/// +/// Groups of organization members that gives permissions on specified repositories. +@JsonSerializable() class Team { Team({ - this.name, + this.description, + this.htmlUrl, this.id, - this.permission, + this.ldapDn, this.membersCount, - this.reposCount, + this.membersUrl, + this.name, + this.nodeId, this.organization, + this.parent, + this.permission, + this.permissions, + this.privacy, + this.reposCount, + this.repositoriesUrl, + this.slug, + this.url, }); - /// Team Name - final String name; + /// Description of the team + /// + /// Example: `A great team.` + String? description; + + /// Format: uri + /// + /// Example: `https://github.com/orgs/rails/teams/core` + String? htmlUrl; - /// Team ID - final int id; + /// Unique identifier of the team + /// + /// Example: `1` + int? id; - /// Team Permission - final String permission; + /// Distinguished Name (DN) that team maps to within LDAP environment + /// + /// Example: `uid=example,ou=users,dc=github,dc=com` + String? ldapDn; /// Number of Members @JsonKey(name: 'members_count') - final int membersCount; + int? membersCount; + + /// Example: `https://api.github.com/organizations/1/team/1/members{/member}` + String? membersUrl; + + /// Name of the team + /// + /// Example: `Justice League` + String? name; + + /// Example: `MDQ6VGVhbTE=` + String? nodeId; + + /// Organization + Organization? organization; + + /// Team Simple + /// + /// Groups of organization members that gives permissions on specified repositories. + Team? parent; + + /// Permission that the team will have for its repositories + /// + /// Example: `admin` + String? permission; + + Permissions? permissions; + + /// The level of privacy this team should have + /// + /// Example: `closed` + String? privacy; /// Number of Repositories @JsonKey(name: 'repos_count') - final int reposCount; + int? reposCount; - /// Organization - final Organization organization; + /// Format: uri + /// + /// Example: `https://api.github.com/organizations/1/team/1/repos` + String? repositoriesUrl; - factory Team.fromJson(Map input) { - return _$TeamFromJson(input); - } + /// Example: `justice-league` + String? slug; + + /// URL for the team + /// + /// Format: uri + /// + /// Example: `https://api.github.com/organizations/1/team/1` + String? url; + + Map toJson() => _$TeamToJson(this); + + factory Team.fromJson(Map input) => _$TeamFromJson(input); +} + +@JsonSerializable() +class Permissions { + Permissions({ + this.admin, + this.maintain, + this.pull, + this.push, + this.triage, + }); + + bool? admin; + bool? maintain; + bool? pull; + bool? push; + bool? triage; + + Map toJson() => _$PermissionsToJson(this); + + factory Permissions.fromJson(Map input) => + _$PermissionsFromJson(input); } /// Model class for the team membership state. class TeamMembershipState { - final String name; - TeamMembershipState(this.name); + String? name; + bool get isPending => name == 'pending'; bool get isActive => name == 'active'; bool get isInactive => name == null; } /// Model class for a team member. -@JsonSerializable(createToJson: false) +@JsonSerializable() class TeamMember { TeamMember( {this.login, @@ -157,58 +249,28 @@ class TeamMember { this.htmlUrl}); /// Member Username - final String login; + String? login; /// Member ID - final int id; + int? id; /// Url to Member Avatar @JsonKey(name: 'avatar_url') - final String avatarUrl; + String? avatarUrl; /// Member Type - final String type; + String? type; /// If the member is a site administrator @JsonKey(name: 'site_admin') - final bool siteAdmin; + bool? siteAdmin; /// Profile of the Member @JsonKey(name: 'html_url') - final String htmlUrl; + String? htmlUrl; factory TeamMember.fromJson(Map input) { return _$TeamMemberFromJson(input); } -} - -/// Model class for a team repository. -@JsonSerializable(createToJson: false) -class TeamRepository extends Repository { - TeamRepository({this.permissions}); - - /// Repository Permissions. - TeamRepositoryPermissions permissions; - - factory TeamRepository.fromJson(Map input) { - return _$TeamRepositoryFromJson(input); - } -} - -/// Model class for team repository permissions. -@JsonSerializable(createToJson: false) -class TeamRepositoryPermissions { - TeamRepositoryPermissions(this.admin, this.push, this.pull); - - /// Administrative Access - final bool admin; - - /// Push Access - final bool push; - - /// Pull Access - final bool pull; - - factory TeamRepositoryPermissions.fromJson(Map json) => - _$TeamRepositoryPermissionsFromJson(json); + Map toJson() => _$TeamMemberToJson(this); } diff --git a/lib/src/common/model/orgs.g.dart b/lib/src/common/model/orgs.g.dart index a488c764..dc9dc349 100644 --- a/lib/src/common/model/orgs.g.dart +++ b/lib/src/common/model/orgs.g.dart @@ -6,29 +6,27 @@ part of 'orgs.dart'; // JsonSerializableGenerator // ************************************************************************** -Organization _$OrganizationFromJson(Map json) { - return Organization( - login: json['login'] as String, - id: json['id'] as int, - htmlUrl: json['html_url'] as String, - avatarUrl: json['avatar_url'] as String, - name: json['name'] as String, - company: json['company'] as String, - blog: json['blog'] as String, - location: json['location'] as String, - email: json['email'] as String, - publicReposCount: json['public_repos'] as int, - publicGistsCount: json['public_gists'] as int, - followersCount: json['followers'] as int, - followingCount: json['following'] as int, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - ); -} +Organization _$OrganizationFromJson(Map json) => Organization( + login: json['login'] as String?, + id: (json['id'] as num?)?.toInt(), + htmlUrl: json['html_url'] as String?, + avatarUrl: json['avatar_url'] as String?, + name: json['name'] as String?, + company: json['company'] as String?, + blog: json['blog'] as String?, + location: json['location'] as String?, + email: json['email'] as String?, + publicReposCount: (json['public_repos'] as num?)?.toInt(), + publicGistsCount: (json['public_gists'] as num?)?.toInt(), + followersCount: (json['followers'] as num?)?.toInt(), + followingCount: (json['following'] as num?)?.toInt(), + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + ); Map _$OrganizationToJson(Organization instance) => { @@ -50,53 +48,99 @@ Map _$OrganizationToJson(Organization instance) => }; OrganizationMembership _$OrganizationMembershipFromJson( - Map json) { - return OrganizationMembership( - state: json['state'] as String, - organization: json['organization'] == null - ? null - : Organization.fromJson(json['organization'] as Map), - ); -} + Map json) => + OrganizationMembership( + state: json['state'] as String?, + organization: json['organization'] == null + ? null + : Organization.fromJson(json['organization'] as Map), + ); -Team _$TeamFromJson(Map json) { - return Team( - name: json['name'] as String, - id: json['id'] as int, - permission: json['permission'] as String, - membersCount: json['members_count'] as int, - reposCount: json['repos_count'] as int, - organization: json['organization'] == null - ? null - : Organization.fromJson(json['organization'] as Map), - ); -} +Map _$OrganizationMembershipToJson( + OrganizationMembership instance) => + { + 'state': instance.state, + 'organization': instance.organization, + }; -TeamMember _$TeamMemberFromJson(Map json) { - return TeamMember( - login: json['login'] as String, - id: json['id'] as int, - avatarUrl: json['avatar_url'] as String, - type: json['type'] as String, - siteAdmin: json['site_admin'] as bool, - htmlUrl: json['html_url'] as String, - ); -} +Team _$TeamFromJson(Map json) => Team( + description: json['description'] as String?, + htmlUrl: json['html_url'] as String?, + id: (json['id'] as num?)?.toInt(), + ldapDn: json['ldap_dn'] as String?, + membersCount: (json['members_count'] as num?)?.toInt(), + membersUrl: json['members_url'] as String?, + name: json['name'] as String?, + nodeId: json['node_id'] as String?, + organization: json['organization'] == null + ? null + : Organization.fromJson(json['organization'] as Map), + parent: json['parent'] == null + ? null + : Team.fromJson(json['parent'] as Map), + permission: json['permission'] as String?, + permissions: json['permissions'] == null + ? null + : Permissions.fromJson(json['permissions'] as Map), + privacy: json['privacy'] as String?, + reposCount: (json['repos_count'] as num?)?.toInt(), + repositoriesUrl: json['repositories_url'] as String?, + slug: json['slug'] as String?, + url: json['url'] as String?, + ); -TeamRepository _$TeamRepositoryFromJson(Map json) { - return TeamRepository( - permissions: json['permissions'] == null - ? null - : TeamRepositoryPermissions.fromJson( - json['permissions'] as Map), - ); -} +Map _$TeamToJson(Team instance) => { + 'description': instance.description, + 'html_url': instance.htmlUrl, + 'id': instance.id, + 'ldap_dn': instance.ldapDn, + 'members_count': instance.membersCount, + 'members_url': instance.membersUrl, + 'name': instance.name, + 'node_id': instance.nodeId, + 'organization': instance.organization, + 'parent': instance.parent, + 'permission': instance.permission, + 'permissions': instance.permissions, + 'privacy': instance.privacy, + 'repos_count': instance.reposCount, + 'repositories_url': instance.repositoriesUrl, + 'slug': instance.slug, + 'url': instance.url, + }; + +Permissions _$PermissionsFromJson(Map json) => Permissions( + admin: json['admin'] as bool?, + maintain: json['maintain'] as bool?, + pull: json['pull'] as bool?, + push: json['push'] as bool?, + triage: json['triage'] as bool?, + ); -TeamRepositoryPermissions _$TeamRepositoryPermissionsFromJson( - Map json) { - return TeamRepositoryPermissions( - json['admin'] as bool, - json['push'] as bool, - json['pull'] as bool, - ); -} +Map _$PermissionsToJson(Permissions instance) => + { + 'admin': instance.admin, + 'maintain': instance.maintain, + 'pull': instance.pull, + 'push': instance.push, + 'triage': instance.triage, + }; + +TeamMember _$TeamMemberFromJson(Map json) => TeamMember( + login: json['login'] as String?, + id: (json['id'] as num?)?.toInt(), + avatarUrl: json['avatar_url'] as String?, + type: json['type'] as String?, + siteAdmin: json['site_admin'] as bool?, + htmlUrl: json['html_url'] as String?, + ); + +Map _$TeamMemberToJson(TeamMember instance) => + { + 'login': instance.login, + 'id': instance.id, + 'avatar_url': instance.avatarUrl, + 'type': instance.type, + 'site_admin': instance.siteAdmin, + 'html_url': instance.htmlUrl, + }; diff --git a/lib/src/common/model/pulls.dart b/lib/src/common/model/pulls.dart index 2ea0365d..2cb10a8e 100644 --- a/lib/src/common/model/pulls.dart +++ b/lib/src/common/model/pulls.dart @@ -1,15 +1,15 @@ import 'package:github/src/common.dart'; -import 'package:github/src/common/model/users.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:meta/meta.dart'; part 'pulls.g.dart'; /// Model class for a Pull Request. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class PullRequest { PullRequest({ this.id, + this.nodeId, this.htmlUrl, this.diffUrl, this.patchUrl, @@ -29,89 +29,125 @@ class PullRequest { this.merged, this.mergeable, this.mergedBy, - this.commentsCount, - this.commitsCount, - this.additionsCount, - this.deletionsCount, - this.changedFilesCount, + this.commentsCount = 0, + this.commitsCount = 0, + this.additionsCount = 0, + this.deletionsCount = 0, + this.changedFilesCount = 0, this.labels, + this.requestedReviewers, + this.reviewCommentCount = 0, + this.milestone, + this.rebaseable = false, + this.mergeableState = '', + this.maintainerCanModify = false, + this.authorAssociation = '', }); /// Pull Request ID - int id; + int? id; + + /// Unique node identification string. + String? nodeId; /// Url to the Pull Request Page - String htmlUrl; + String? htmlUrl; /// Url to the diff for this Pull Request - String diffUrl; + String? diffUrl; /// Url to the patch for this Pull Request - String patchUrl; + String? patchUrl; /// Pull Request Number - int number; + int? number; /// Pull Request State - String state; + String? state; /// Pull Request Title - String title; + String? title; /// Pull Request Body - String body; + String? body; /// Time the pull request was created - DateTime createdAt; + DateTime? createdAt; /// Time the pull request was updated - DateTime updatedAt; + DateTime? updatedAt; /// Time the pull request was closed - DateTime closedAt; + DateTime? closedAt; /// Time the pull request was merged - DateTime mergedAt; + DateTime? mergedAt; /// The Pull Request Head - PullRequestHead head; + PullRequestHead? head; /// Pull Request Base - PullRequestHead base; + PullRequestHead? base; /// The User who created the Pull Request - User user; + User? user; /// Whether or not the pull request is a draft - bool draft; - String mergeCommitSha; + bool? draft; + + String? mergeCommitSha; /// If the pull request was merged - bool merged; + bool? merged; /// If the pull request is mergeable - bool mergeable; + bool? mergeable; /// The user who merged the pull request - User mergedBy; + User? mergedBy; /// Number of comments - int commentsCount; + @JsonKey(name: 'comments') + int? commentsCount; /// Number of commits - int commitsCount; + @JsonKey(name: 'commits') + int? commitsCount; /// Number of additions - int additionsCount; + @JsonKey(name: 'additions') + int? additionsCount; /// Number of deletions - int deletionsCount; + @JsonKey(name: 'deletions') + int? deletionsCount; /// Number of changed files - int changedFilesCount; + @JsonKey(name: 'changed_files') + int? changedFilesCount; /// Pull Request Labels - List labels; + List? labels; + + /// Reviewers requested for this Pull Request. + List? requestedReviewers; + + /// The number of review comments on the Pull Request. + @JsonKey(name: 'review_comments') + int? reviewCommentCount; + + Milestone? milestone; + + bool? rebaseable; + + String? mergeableState; + + bool? maintainerCanModify; + + /// Ex: CONTRIBUTOR, NONE, OWNER + String? authorAssociation; + + Repository? repo; factory PullRequest.fromJson(Map input) => _$PullRequestFromJson(input); @@ -119,16 +155,17 @@ class PullRequest { } /// Model class for a pull request merge. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class PullRequestMerge { PullRequestMerge({ this.merged, this.sha, this.message, }); - bool merged; - String sha; - String message; + + bool? merged; + String? sha; + String? message; factory PullRequestMerge.fromJson(Map input) => _$PullRequestMergeFromJson(input); @@ -136,7 +173,7 @@ class PullRequestMerge { } /// Model class for a Pull Request Head. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class PullRequestHead { PullRequestHead({ this.label, @@ -146,11 +183,11 @@ class PullRequestHead { this.repo, }); - String label; - String ref; - String sha; - User user; - Repository repo; + String? label; + String? ref; + String? sha; + User? user; + Repository? repo; factory PullRequestHead.fromJson(Map input) => _$PullRequestHeadFromJson(input); @@ -158,23 +195,23 @@ class PullRequestHead { } /// Model class for a pull request to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreatePullRequest { CreatePullRequest(this.title, this.head, this.base, {this.draft = false, this.body}); - final String title; - final String head; - final String base; + String? title; + String? head; + String? base; /// Whether a draft PR should be created. /// /// This is currently experimental functionality since the way draft PRs are /// created through Github's REST API is in developer preview only - and could change at any time. @experimental - final bool draft; + bool? draft; - String body; + String? body; factory CreatePullRequest.fromJson(Map input) => _$CreatePullRequestFromJson(input); @@ -182,7 +219,7 @@ class CreatePullRequest { } /// Model class for a pull request comment. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class PullRequestComment { PullRequestComment({ this.id, @@ -200,21 +237,22 @@ class PullRequestComment { this.pullRequestUrl, this.links, }); - int id; - String diffHunk; - String path; - int position; - int originalPosition; - String commitId; - String originalCommitId; - User user; - String body; - DateTime createdAt; - DateTime updatedAt; - String url; - String pullRequestUrl; + + int? id; + String? diffHunk; + String? path; + int? position; + int? originalPosition; + String? commitId; + String? originalCommitId; + User? user; + String? body; + DateTime? createdAt; + DateTime? updatedAt; + String? url; + String? pullRequestUrl; @JsonKey(name: '_links') - Links links; + Links? links; factory PullRequestComment.fromJson(Map input) => _$PullRequestCommentFromJson(input); @@ -222,20 +260,20 @@ class PullRequestComment { } /// Model class for a pull request comment to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreatePullRequestComment { CreatePullRequestComment(this.body, this.commitId, this.path, this.position); - String body; - String commitId; - String path; - int position; + String? body; + String? commitId; + String? path; + int? position; factory CreatePullRequestComment.fromJson(Map input) => _$CreatePullRequestCommentFromJson(input); Map toJson() => _$CreatePullRequestCommentToJson(this); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class PullRequestFile { PullRequestFile({ this.sha, @@ -249,21 +287,257 @@ class PullRequestFile { this.contentsUrl, this.patch, }); - String sha; - String filename; - String status; + + String? sha; + String? filename; + String? status; @JsonKey(name: 'additions') - int additionsCount; + int? additionsCount; @JsonKey(name: 'deletions') - int deletionsCount; + int? deletionsCount; @JsonKey(name: 'changes') - int changesCount; - String blobUrl; - String rawUrl; - String contentsUrl; - String patch; + int? changesCount; + String? blobUrl; + String? rawUrl; + String? contentsUrl; + String? patch; factory PullRequestFile.fromJson(Map input) => _$PullRequestFileFromJson(input); Map toJson() => _$PullRequestFileToJson(this); } + +@JsonSerializable() +class PullRequestReview { + PullRequestReview( + {required this.id, + this.user, + this.body, + this.state, + this.htmlUrl, + this.pullRequestUrl}); + + int id; + User? user; + String? body; + String? state; + String? htmlUrl; + String? pullRequestUrl; + DateTime? submittedAt; + String? authorAssociation; + String? commitId; + + factory PullRequestReview.fromJson(Map input) => + _$PullRequestReviewFromJson(input); + Map toJson() => _$PullRequestReviewToJson(this); +} + +@JsonSerializable() +class CreatePullRequestReview { + CreatePullRequestReview(this.owner, this.repo, this.pullNumber, this.event, + {this.body, this.comments}); + + String owner; + String repo; + String event; + String? body; + int pullNumber; + List? comments; + + factory CreatePullRequestReview.fromJson(Map input) => + _$CreatePullRequestReviewFromJson(input); + Map toJson() => _$CreatePullRequestReviewToJson(this); +} + +/// Pull Request Review Comment +/// +/// Pull Request Review Comments are comments on a portion of the Pull Request's +/// diff. +@JsonSerializable() +class PullRequestReviewComment { + PullRequestReviewComment({ + this.authorAssociation, + this.body, + this.bodyHtml, + this.bodyText, + this.commitId, + this.createdAt, + this.diffHunk, + this.htmlUrl, + this.id, + this.inReplyToId, + this.line, + this.links, + this.nodeId, + this.originalCommitId, + this.originalLine, + this.originalPosition, + this.originalStartLine, + this.path, + this.position, + this.pullRequestReviewId, + this.pullRequestUrl, + this.reactions, + this.side, + this.startLine, + this.startSide, + this.subjectType, + this.updatedAt, + this.url, + this.user, + }); + + /// How the author is associated with the repository. + /// + /// Example: `OWNER` + String? authorAssociation; + + /// The text of the comment. + /// + /// Example: `We should probably include a check for null values here.` + String? body; + + /// Example: `"

comment body

"` + String? bodyHtml; + + /// Example: `"comment body"` + String? bodyText; + + /// The SHA of the commit to which the comment applies. + /// + /// Example: `6dcb09b5b57875f334f61aebed695e2e4193db5e` + String? commitId; + + DateTime? createdAt; + + /// The diff of the line that the comment refers to. + /// + /// Example: `@@ -16,33 +16,40 @@ public class Connection : IConnection...` + String? diffHunk; + + /// HTML URL for the pull request review comment. + /// + /// Example: `https://github.com/octocat/Hello-World/pull/1#discussion-diff-1` + String? htmlUrl; + + /// The ID of the pull request review comment. + int? id; + + /// The comment ID to reply to. + int? inReplyToId; + + /// The line of the blob to which the comment applies. The last line of the range + /// for a multi-line comment + int? line; + + @JsonKey(name: '_links') + ReviewLinks? links; + + /// The node ID of the pull request review comment. + /// + /// Example: `MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDEw` + String? nodeId; + + /// The SHA of the original commit to which the comment applies. + /// + /// Example: `9c48853fa3dc5c1c3d6f1f1cd1f2743e72652840` + String? originalCommitId; + + /// The line of the blob to which the comment applies. The last line of the range + /// for a multi-line comment + int? originalLine; + + /// The index of the original line in the diff to which the comment applies. This + /// field is deprecated; use `original_line` instead. + int? originalPosition; + + /// The first line of the range for a multi-line comment. + int? originalStartLine; + + /// The relative path of the file to which the comment applies. + /// + /// Example: `config/database.yaml` + String? path; + + /// The line index in the diff to which the comment applies. This field is deprecated; + /// use `line` instead. + int? position; + + /// The ID of the pull request review to which the comment belongs. + int? pullRequestReviewId; + + /// URL for the pull request that the review comment belongs to. + /// + /// Example: `https://api.github.com/repos/octocat/Hello-World/pulls/1` + String? pullRequestUrl; + + /// Reaction Rollup + ReactionRollup? reactions; + + /// The side of the diff to which the comment applies. The side of the last line + /// of the range for a multi-line comment + String? side; + + /// The first line of the range for a multi-line comment. + int? startLine; + + /// The side of the first line of the range for a multi-line comment. + String? startSide; + + /// The level at which the comment is targeted, can be a diff line or a file. + String? subjectType; + + DateTime? updatedAt; + + /// URL for the pull request review comment + /// + /// Example: `https://api.github.com/repos/octocat/Hello-World/pulls/comments/1` + String? url; + + User? user; + + Map toJson() => _$PullRequestReviewCommentToJson(this); + + factory PullRequestReviewComment.fromJson(Map input) => + _$PullRequestReviewCommentFromJson(input); +} + +/// This is similar to [Links] but represents a different serialization +/// in the GitHub API. +/// +/// It is used for [PullRequestReviewComment.links] and +/// [ReviewEvent.links]. +class ReviewLinks { + ReviewLinks({ + this.html, + this.pullRequest, + this.self, + }); + + Uri? html; + Uri? pullRequest; + Uri? self; + + Map toJson() { + return { + 'html': {'href': html?.toString()}, + 'pullRequest': {'href': pullRequest?.toString()}, + 'self': {'href': self?.toString()}, + }; + } + + static Uri? _parseBlock(Map input, String key) { + if (input[key] is Map && input[key]['href'] is String) { + return Uri.parse(input[key]['href']! as String); + } + return null; + } + + factory ReviewLinks.fromJson(Map input) { + return ReviewLinks( + html: _parseBlock(input, 'html'), + pullRequest: _parseBlock(input, 'pull_request'), + self: _parseBlock(input, 'self'), + ); + } +} diff --git a/lib/src/common/model/pulls.g.dart b/lib/src/common/model/pulls.g.dart index bc9b1293..e7dea0cb 100644 --- a/lib/src/common/model/pulls.g.dart +++ b/lib/src/common/model/pulls.g.dart @@ -6,59 +6,71 @@ part of 'pulls.dart'; // JsonSerializableGenerator // ************************************************************************** -PullRequest _$PullRequestFromJson(Map json) { - return PullRequest( - id: json['id'] as int, - htmlUrl: json['html_url'] as String, - diffUrl: json['diff_url'] as String, - patchUrl: json['patch_url'] as String, - number: json['number'] as int, - state: json['state'] as String, - title: json['title'] as String, - body: json['body'] as String, - createdAt: json['created_at'] == null +PullRequest _$PullRequestFromJson(Map json) => PullRequest( + id: (json['id'] as num?)?.toInt(), + nodeId: json['node_id'] as String?, + htmlUrl: json['html_url'] as String?, + diffUrl: json['diff_url'] as String?, + patchUrl: json['patch_url'] as String?, + number: (json['number'] as num?)?.toInt(), + state: json['state'] as String?, + title: json['title'] as String?, + body: json['body'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + closedAt: json['closed_at'] == null + ? null + : DateTime.parse(json['closed_at'] as String), + mergedAt: json['merged_at'] == null + ? null + : DateTime.parse(json['merged_at'] as String), + head: json['head'] == null + ? null + : PullRequestHead.fromJson(json['head'] as Map), + base: json['base'] == null + ? null + : PullRequestHead.fromJson(json['base'] as Map), + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + draft: json['draft'] as bool?, + mergeCommitSha: json['merge_commit_sha'] as String?, + merged: json['merged'] as bool?, + mergeable: json['mergeable'] as bool?, + mergedBy: json['merged_by'] == null + ? null + : User.fromJson(json['merged_by'] as Map), + commentsCount: (json['comments'] as num?)?.toInt() ?? 0, + commitsCount: (json['commits'] as num?)?.toInt() ?? 0, + additionsCount: (json['additions'] as num?)?.toInt() ?? 0, + deletionsCount: (json['deletions'] as num?)?.toInt() ?? 0, + changedFilesCount: (json['changed_files'] as num?)?.toInt() ?? 0, + labels: (json['labels'] as List?) + ?.map((e) => IssueLabel.fromJson(e as Map)) + .toList(), + requestedReviewers: (json['requested_reviewers'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList(), + reviewCommentCount: (json['review_comments'] as num?)?.toInt() ?? 0, + milestone: json['milestone'] == null + ? null + : Milestone.fromJson(json['milestone'] as Map), + rebaseable: json['rebaseable'] as bool? ?? false, + mergeableState: json['mergeable_state'] as String? ?? '', + maintainerCanModify: json['maintainer_can_modify'] as bool? ?? false, + authorAssociation: json['author_association'] as String? ?? '', + )..repo = json['repo'] == null ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - closedAt: json['closed_at'] == null - ? null - : DateTime.parse(json['closed_at'] as String), - mergedAt: json['merged_at'] == null - ? null - : DateTime.parse(json['merged_at'] as String), - head: json['head'] == null - ? null - : PullRequestHead.fromJson(json['head'] as Map), - base: json['base'] == null - ? null - : PullRequestHead.fromJson(json['base'] as Map), - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - draft: json['draft'] as bool, - mergeCommitSha: json['merge_commit_sha'] as String, - merged: json['merged'] as bool, - mergeable: json['mergeable'] as bool, - mergedBy: json['merged_by'] == null - ? null - : User.fromJson(json['merged_by'] as Map), - commentsCount: json['comments_count'] as int, - commitsCount: json['commits_count'] as int, - additionsCount: json['additions_count'] as int, - deletionsCount: json['deletions_count'] as int, - changedFilesCount: json['changed_files_count'] as int, - labels: (json['labels'] as List) - ?.map((e) => - e == null ? null : IssueLabel.fromJson(e as Map)) - ?.toList(), - ); -} + : Repository.fromJson(json['repo'] as Map); Map _$PullRequestToJson(PullRequest instance) => { 'id': instance.id, + 'node_id': instance.nodeId, 'html_url': instance.htmlUrl, 'diff_url': instance.diffUrl, 'patch_url': instance.patchUrl, @@ -78,21 +90,28 @@ Map _$PullRequestToJson(PullRequest instance) => 'merged': instance.merged, 'mergeable': instance.mergeable, 'merged_by': instance.mergedBy, - 'comments_count': instance.commentsCount, - 'commits_count': instance.commitsCount, - 'additions_count': instance.additionsCount, - 'deletions_count': instance.deletionsCount, - 'changed_files_count': instance.changedFilesCount, + 'comments': instance.commentsCount, + 'commits': instance.commitsCount, + 'additions': instance.additionsCount, + 'deletions': instance.deletionsCount, + 'changed_files': instance.changedFilesCount, 'labels': instance.labels, + 'requested_reviewers': instance.requestedReviewers, + 'review_comments': instance.reviewCommentCount, + 'milestone': instance.milestone, + 'rebaseable': instance.rebaseable, + 'mergeable_state': instance.mergeableState, + 'maintainer_can_modify': instance.maintainerCanModify, + 'author_association': instance.authorAssociation, + 'repo': instance.repo, }; -PullRequestMerge _$PullRequestMergeFromJson(Map json) { - return PullRequestMerge( - merged: json['merged'] as bool, - sha: json['sha'] as String, - message: json['message'] as String, - ); -} +PullRequestMerge _$PullRequestMergeFromJson(Map json) => + PullRequestMerge( + merged: json['merged'] as bool?, + sha: json['sha'] as String?, + message: json['message'] as String?, + ); Map _$PullRequestMergeToJson(PullRequestMerge instance) => { @@ -101,19 +120,18 @@ Map _$PullRequestMergeToJson(PullRequestMerge instance) => 'message': instance.message, }; -PullRequestHead _$PullRequestHeadFromJson(Map json) { - return PullRequestHead( - label: json['label'] as String, - ref: json['ref'] as String, - sha: json['sha'] as String, - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - repo: json['repo'] == null - ? null - : Repository.fromJson(json['repo'] as Map), - ); -} +PullRequestHead _$PullRequestHeadFromJson(Map json) => + PullRequestHead( + label: json['label'] as String?, + ref: json['ref'] as String?, + sha: json['sha'] as String?, + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + repo: json['repo'] == null + ? null + : Repository.fromJson(json['repo'] as Map), + ); Map _$PullRequestHeadToJson(PullRequestHead instance) => { @@ -124,15 +142,14 @@ Map _$PullRequestHeadToJson(PullRequestHead instance) => 'repo': instance.repo, }; -CreatePullRequest _$CreatePullRequestFromJson(Map json) { - return CreatePullRequest( - json['title'] as String, - json['head'] as String, - json['base'] as String, - draft: json['draft'] as bool, - body: json['body'] as String, - ); -} +CreatePullRequest _$CreatePullRequestFromJson(Map json) => + CreatePullRequest( + json['title'] as String?, + json['head'] as String?, + json['base'] as String?, + draft: json['draft'] as bool? ?? false, + body: json['body'] as String?, + ); Map _$CreatePullRequestToJson(CreatePullRequest instance) => { @@ -143,32 +160,31 @@ Map _$CreatePullRequestToJson(CreatePullRequest instance) => 'body': instance.body, }; -PullRequestComment _$PullRequestCommentFromJson(Map json) { - return PullRequestComment( - id: json['id'] as int, - diffHunk: json['diff_hunk'] as String, - path: json['path'] as String, - position: json['position'] as int, - originalPosition: json['original_position'] as int, - commitId: json['commit_id'] as String, - originalCommitId: json['original_commit_id'] as String, - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - body: json['body'] as String, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - url: json['url'] as String, - pullRequestUrl: json['pull_request_url'] as String, - links: json['_links'] == null - ? null - : Links.fromJson(json['_links'] as Map), - ); -} +PullRequestComment _$PullRequestCommentFromJson(Map json) => + PullRequestComment( + id: (json['id'] as num?)?.toInt(), + diffHunk: json['diff_hunk'] as String?, + path: json['path'] as String?, + position: (json['position'] as num?)?.toInt(), + originalPosition: (json['original_position'] as num?)?.toInt(), + commitId: json['commit_id'] as String?, + originalCommitId: json['original_commit_id'] as String?, + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + body: json['body'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + url: json['url'] as String?, + pullRequestUrl: json['pull_request_url'] as String?, + links: json['_links'] == null + ? null + : Links.fromJson(json['_links'] as Map), + ); Map _$PullRequestCommentToJson(PullRequestComment instance) => { @@ -189,14 +205,13 @@ Map _$PullRequestCommentToJson(PullRequestComment instance) => }; CreatePullRequestComment _$CreatePullRequestCommentFromJson( - Map json) { - return CreatePullRequestComment( - json['body'] as String, - json['commit_id'] as String, - json['path'] as String, - json['position'] as int, - ); -} + Map json) => + CreatePullRequestComment( + json['body'] as String?, + json['commit_id'] as String?, + json['path'] as String?, + (json['position'] as num?)?.toInt(), + ); Map _$CreatePullRequestCommentToJson( CreatePullRequestComment instance) => @@ -207,20 +222,19 @@ Map _$CreatePullRequestCommentToJson( 'position': instance.position, }; -PullRequestFile _$PullRequestFileFromJson(Map json) { - return PullRequestFile( - sha: json['sha'] as String, - filename: json['filename'] as String, - status: json['status'] as String, - additionsCount: json['additions'] as int, - deletionsCount: json['deletions'] as int, - changesCount: json['changes'] as int, - blobUrl: json['blob_url'] as String, - rawUrl: json['raw_url'] as String, - contentsUrl: json['contents_url'] as String, - patch: json['patch'] as String, - ); -} +PullRequestFile _$PullRequestFileFromJson(Map json) => + PullRequestFile( + sha: json['sha'] as String?, + filename: json['filename'] as String?, + status: json['status'] as String?, + additionsCount: (json['additions'] as num?)?.toInt(), + deletionsCount: (json['deletions'] as num?)?.toInt(), + changesCount: (json['changes'] as num?)?.toInt(), + blobUrl: json['blob_url'] as String?, + rawUrl: json['raw_url'] as String?, + contentsUrl: json['contents_url'] as String?, + patch: json['patch'] as String?, + ); Map _$PullRequestFileToJson(PullRequestFile instance) => { @@ -235,3 +249,136 @@ Map _$PullRequestFileToJson(PullRequestFile instance) => 'contents_url': instance.contentsUrl, 'patch': instance.patch, }; + +PullRequestReview _$PullRequestReviewFromJson(Map json) => + PullRequestReview( + id: (json['id'] as num).toInt(), + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + body: json['body'] as String?, + state: json['state'] as String?, + htmlUrl: json['html_url'] as String?, + pullRequestUrl: json['pull_request_url'] as String?, + ) + ..submittedAt = json['submitted_at'] == null + ? null + : DateTime.parse(json['submitted_at'] as String) + ..authorAssociation = json['author_association'] as String? + ..commitId = json['commit_id'] as String?; + +Map _$PullRequestReviewToJson(PullRequestReview instance) => + { + 'id': instance.id, + 'user': instance.user, + 'body': instance.body, + 'state': instance.state, + 'html_url': instance.htmlUrl, + 'pull_request_url': instance.pullRequestUrl, + 'submitted_at': instance.submittedAt?.toIso8601String(), + 'author_association': instance.authorAssociation, + 'commit_id': instance.commitId, + }; + +CreatePullRequestReview _$CreatePullRequestReviewFromJson( + Map json) => + CreatePullRequestReview( + json['owner'] as String, + json['repo'] as String, + (json['pull_number'] as num).toInt(), + json['event'] as String, + body: json['body'] as String?, + comments: (json['comments'] as List?) + ?.map((e) => + PullRequestReviewComment.fromJson(e as Map)) + .toList(), + ); + +Map _$CreatePullRequestReviewToJson( + CreatePullRequestReview instance) => + { + 'owner': instance.owner, + 'repo': instance.repo, + 'event': instance.event, + 'body': instance.body, + 'pull_number': instance.pullNumber, + 'comments': instance.comments, + }; + +PullRequestReviewComment _$PullRequestReviewCommentFromJson( + Map json) => + PullRequestReviewComment( + authorAssociation: json['author_association'] as String?, + body: json['body'] as String?, + bodyHtml: json['body_html'] as String?, + bodyText: json['body_text'] as String?, + commitId: json['commit_id'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + diffHunk: json['diff_hunk'] as String?, + htmlUrl: json['html_url'] as String?, + id: (json['id'] as num?)?.toInt(), + inReplyToId: (json['in_reply_to_id'] as num?)?.toInt(), + line: (json['line'] as num?)?.toInt(), + links: json['_links'] == null + ? null + : ReviewLinks.fromJson(json['_links'] as Map), + nodeId: json['node_id'] as String?, + originalCommitId: json['original_commit_id'] as String?, + originalLine: (json['original_line'] as num?)?.toInt(), + originalPosition: (json['original_position'] as num?)?.toInt(), + originalStartLine: (json['original_start_line'] as num?)?.toInt(), + path: json['path'] as String?, + position: (json['position'] as num?)?.toInt(), + pullRequestReviewId: (json['pull_request_review_id'] as num?)?.toInt(), + pullRequestUrl: json['pull_request_url'] as String?, + reactions: json['reactions'] == null + ? null + : ReactionRollup.fromJson(json['reactions'] as Map), + side: json['side'] as String?, + startLine: (json['start_line'] as num?)?.toInt(), + startSide: json['start_side'] as String?, + subjectType: json['subject_type'] as String?, + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + url: json['url'] as String?, + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + ); + +Map _$PullRequestReviewCommentToJson( + PullRequestReviewComment instance) => + { + 'author_association': instance.authorAssociation, + 'body': instance.body, + 'body_html': instance.bodyHtml, + 'body_text': instance.bodyText, + 'commit_id': instance.commitId, + 'created_at': instance.createdAt?.toIso8601String(), + 'diff_hunk': instance.diffHunk, + 'html_url': instance.htmlUrl, + 'id': instance.id, + 'in_reply_to_id': instance.inReplyToId, + 'line': instance.line, + '_links': instance.links, + 'node_id': instance.nodeId, + 'original_commit_id': instance.originalCommitId, + 'original_line': instance.originalLine, + 'original_position': instance.originalPosition, + 'original_start_line': instance.originalStartLine, + 'path': instance.path, + 'position': instance.position, + 'pull_request_review_id': instance.pullRequestReviewId, + 'pull_request_url': instance.pullRequestUrl, + 'reactions': instance.reactions, + 'side': instance.side, + 'start_line': instance.startLine, + 'start_side': instance.startSide, + 'subject_type': instance.subjectType, + 'updated_at': instance.updatedAt?.toIso8601String(), + 'url': instance.url, + 'user': instance.user, + }; diff --git a/lib/src/common/model/reaction.dart b/lib/src/common/model/reaction.dart index 4b1ad1c7..a9b73d7f 100644 --- a/lib/src/common/model/reaction.dart +++ b/lib/src/common/model/reaction.dart @@ -8,14 +8,8 @@ part 'reaction.g.dart'; /// This API is currently in preview. It may break. /// /// See https://developer.github.com/v3/reactions/ -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class Reaction { - final int id; - final String nodeId; - final User user; - final String content; - final DateTime createdAt; - Reaction({ this.id, this.nodeId, @@ -24,7 +18,13 @@ class Reaction { this.createdAt, }); - ReactionType get type => ReactionType.fromString(content); + int? id; + String? nodeId; + User? user; + String? content; + DateTime? createdAt; + + ReactionType? get type => ReactionType.fromString(content); factory Reaction.fromJson(Map json) => _$ReactionFromJson(json); @@ -34,9 +34,10 @@ class Reaction { @immutable class ReactionType { + const ReactionType._(this.content, this.emoji); + final String content; final String emoji; - const ReactionType._(this.content, this.emoji); @override String toString() => content; @@ -69,5 +70,39 @@ class ReactionType { ':eyes:': eyes, }; - static ReactionType fromString(String content) => _types[content]; + static ReactionType? fromString(String? content) => _types[content!]; +} + +@JsonSerializable() +class ReactionRollup { + ReactionRollup({ + this.plusOne, + this.minusOne, + this.confused, + this.eyes, + this.heart, + this.hooray, + this.laugh, + this.rocket, + this.totalCount, + this.url, + }); + + @JsonKey(name: '+1') + int? plusOne; + @JsonKey(name: '-1') + int? minusOne; + int? confused; + int? eyes; + int? heart; + int? hooray; + int? laugh; + int? rocket; + int? totalCount; + String? url; + + Map toJson() => _$ReactionRollupToJson(this); + + factory ReactionRollup.fromJson(Map input) => + _$ReactionRollupFromJson(input); } diff --git a/lib/src/common/model/reaction.g.dart b/lib/src/common/model/reaction.g.dart index 632d7c7f..4647e160 100644 --- a/lib/src/common/model/reaction.g.dart +++ b/lib/src/common/model/reaction.g.dart @@ -6,19 +6,17 @@ part of 'reaction.dart'; // JsonSerializableGenerator // ************************************************************************** -Reaction _$ReactionFromJson(Map json) { - return Reaction( - id: json['id'] as int, - nodeId: json['node_id'] as String, - user: json['user'] == null - ? null - : User.fromJson(json['user'] as Map), - content: json['content'] as String, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - ); -} +Reaction _$ReactionFromJson(Map json) => Reaction( + id: (json['id'] as num?)?.toInt(), + nodeId: json['node_id'] as String?, + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + content: json['content'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + ); Map _$ReactionToJson(Reaction instance) => { 'id': instance.id, @@ -27,3 +25,31 @@ Map _$ReactionToJson(Reaction instance) => { 'content': instance.content, 'created_at': instance.createdAt?.toIso8601String(), }; + +ReactionRollup _$ReactionRollupFromJson(Map json) => + ReactionRollup( + plusOne: (json['+1'] as num?)?.toInt(), + minusOne: (json['-1'] as num?)?.toInt(), + confused: (json['confused'] as num?)?.toInt(), + eyes: (json['eyes'] as num?)?.toInt(), + heart: (json['heart'] as num?)?.toInt(), + hooray: (json['hooray'] as num?)?.toInt(), + laugh: (json['laugh'] as num?)?.toInt(), + rocket: (json['rocket'] as num?)?.toInt(), + totalCount: (json['total_count'] as num?)?.toInt(), + url: json['url'] as String?, + ); + +Map _$ReactionRollupToJson(ReactionRollup instance) => + { + '+1': instance.plusOne, + '-1': instance.minusOne, + 'confused': instance.confused, + 'eyes': instance.eyes, + 'heart': instance.heart, + 'hooray': instance.hooray, + 'laugh': instance.laugh, + 'rocket': instance.rocket, + 'total_count': instance.totalCount, + 'url': instance.url, + }; diff --git a/lib/src/common/model/repos.dart b/lib/src/common/model/repos.dart index dbf8ea7c..d09c1a7e 100644 --- a/lib/src/common/model/repos.dart +++ b/lib/src/common/model/repos.dart @@ -1,20 +1,24 @@ import 'package:github/src/common.dart'; import 'package:json_annotation/json_annotation.dart'; + part 'repos.g.dart'; -@JsonSerializable(createToJson: false, fieldRename: FieldRename.snake) +@JsonSerializable() class GitHubComparison { - final String url; - final String status; - final int aheadBy; - final int behindBy; - final int totalCommits; + GitHubComparison(this.url, this.status, this.aheadBy, this.behindBy, + this.totalCommits, this.files, this.commits); - GitHubComparison( - this.url, this.status, this.aheadBy, this.behindBy, this.totalCommits); + String? url; + String? status; + int? aheadBy; + int? behindBy; + int? totalCommits; + List? files; + List? commits; factory GitHubComparison.fromJson(Map json) => _$GitHubComparisonFromJson(json); + Map toJson() => _$GitHubComparisonToJson(this); @override String toString() { @@ -34,185 +38,482 @@ class GitHubComparison { } /// Model class for a repository. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class Repository { Repository({ - this.name, - this.id, - this.fullName, + this.name = '', + this.id = 0, + this.fullName = '', this.owner, - this.isPrivate, - this.isFork, - this.htmlUrl, - this.description, - this.cloneUrl, - this.gitUrl, - this.sshUrl, - this.svnUrl, - this.homepage, - this.size, - this.stargazersCount, - this.watchersCount, - this.language, - this.hasIssues, - this.hasWiki, - this.hasDownloads, - this.forksCount, - this.openIssuesCount, - this.defaultBranch, - this.subscribersCount, - this.networkCount, + this.htmlUrl = '', + this.description = '', + this.cloneUrl = '', + this.gitUrl = '', + this.sshUrl = '', + this.svnUrl = '', + this.defaultBranch = '', this.createdAt, + this.isPrivate = false, + this.isFork = false, + this.stargazersCount = 0, + this.watchersCount = 0, + this.language = '', + this.hasWiki = false, + this.hasDownloads = false, + this.forksCount = 0, + this.openIssuesCount = 0, + this.subscribersCount = 0, + this.networkCount = 0, + this.hasIssues = false, + this.size = 0, + this.archived = false, + this.disabled = false, + this.homepage = '', this.updatedAt, this.pushedAt, this.license, - this.archived, - this.disabled, + this.hasPages = false, + this.permissions, + + // Properties from the Timeline API + this.allowAutoMerge, + this.allowForking, + this.allowMergeCommit, + this.allowRebaseMerge, + this.allowSquashMerge, + this.allowUpdateBranch, + this.anonymousAccessEnabled, + this.archiveUrl, + this.assigneesUrl, + this.blobsUrl, + this.branchesUrl, + this.collaboratorsUrl, + this.commentsUrl, + this.commitsUrl, + this.compareUrl, + this.contentsUrl, + this.contributorsUrl, + this.deleteBranchOnMerge, + this.deploymentsUrl, + this.downloadsUrl, + this.eventsUrl, + this.forks, + this.forksUrl, + this.gitCommitsUrl, + this.gitRefsUrl, + this.gitTagsUrl, + this.hasDiscussions, + this.hasProjects, + this.hooksUrl, + this.isTemplate, + this.issueCommentUrl, + this.issueEventsUrl, + this.issuesUrl, + this.keysUrl, + this.labelsUrl, + this.languagesUrl, + this.masterBranch, + this.mergeCommitMessage, + this.mergeCommitTitle, + this.mergesUrl, + this.milestonesUrl, + this.mirrorUrl, + this.nodeId, + this.notificationsUrl, + this.openIssues, + this.organization, + this.pullsUrl, + this.releasesUrl, + this.squashMergeCommitMessage, + this.squashMergeCommitTitle, + this.stargazersUrl, + this.starredAt, + this.statusesUrl, + this.subscribersUrl, + this.subscriptionUrl, + this.tagsUrl, + this.teamsUrl, + this.tempCloneToken, + this.templateRepository, + this.topics, + this.treesUrl, + this.url, + this.visibility, + this.watchers, + this.webCommitSignoffRequired, }); /// Repository Name - final String name; + String name; /// Repository ID - final int id; + int id; /// Full Repository Name - final String fullName; + String fullName; /// Repository Owner - final UserInformation owner; + @JsonKey(defaultValue: null) + UserInformation? owner; /// If the Repository is Private @JsonKey(name: 'private') - final bool isPrivate; + bool isPrivate; /// If the Repository is a fork @JsonKey(name: 'fork') - final bool isFork; + bool isFork; /// Url to the GitHub Repository Page - final String htmlUrl; + String htmlUrl; /// Repository Description - final String description; + String description; // https clone URL - final String cloneUrl; + String cloneUrl; - final String sshUrl; + String sshUrl; - final String svnUrl; + String svnUrl; - final String gitUrl; + String gitUrl; /// Url to the Repository Homepage - final String homepage; + String homepage; /// Repository Size - final int size; + // + /// The size of the repository. Size is calculated hourly. When a repository is + /// initially created, the size is 0. + int size; /// Repository Stars - @JsonKey(name: 'stargazers_count') - final int stargazersCount; + int stargazersCount; /// Repository Watchers - @JsonKey(name: 'watchers_count') - final int watchersCount; + int watchersCount; /// Repository Language - final String language; + String language; /// If the Repository has Issues Enabled - @JsonKey(name: 'has_issues') - final bool hasIssues; + bool hasIssues; /// If the Repository has the Wiki Enabled - @JsonKey(name: 'has_wiki') - final bool hasWiki; + bool hasWiki; /// If the Repository has any Downloads - @JsonKey(name: 'has_downloads') - final bool hasDownloads; + bool hasDownloads; + + /// If the Repository has any Github Pages + bool hasPages; /// Number of Forks - @JsonKey(name: 'forks_count') - final int forksCount; + int forksCount; /// Number of Open Issues - @JsonKey(name: 'open_issues_count') - final int openIssuesCount; + int openIssuesCount; /// Repository Default Branch - @JsonKey(name: 'default_branch') - final String defaultBranch; + String defaultBranch; /// Number of Subscribers - @JsonKey(name: 'subscribers_count') - final int subscribersCount; + int subscribersCount; /// Number of users in the network - @JsonKey(name: 'network_count') - final int networkCount; + int networkCount; /// The time the repository was created at - @JsonKey(name: 'created_at') - final DateTime createdAt; + DateTime? createdAt; /// The last time the repository was pushed at - @JsonKey(name: 'pushed_at') - final DateTime pushedAt; + DateTime? pushedAt; + + DateTime? updatedAt; + + LicenseKind? license; + + bool archived; + + bool disabled; + + RepositoryPermissions? permissions; + + // The following properties were added to support the Timeline API. + + /// Whether to allow Auto-merge to be used on pull requests. + bool? allowAutoMerge; + + /// Whether to allow forking this repo + bool? allowForking; + + /// Whether to allow merge commits for pull requests. + bool? allowMergeCommit; + + /// Whether to allow rebase merges for pull requests. + bool? allowRebaseMerge; + + /// Whether to allow squash merges for pull requests. + bool? allowSquashMerge; + + /// Whether or not a pull request head branch that is behind its base branch can + /// always be updated even if it is not required to be up to date before merging. + bool? allowUpdateBranch; + + /// Whether anonymous git access is enabled for this repository + bool? anonymousAccessEnabled; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}` + String? archiveUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/assignees{/user}` + String? assigneesUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}` + String? blobsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/branches{/branch}` + String? branchesUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}` + String? collaboratorsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/comments{/number}` + String? commentsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/commits{/sha}` + String? commitsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}` + String? compareUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/contents/{+path}` + String? contentsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/contributors` + String? contributorsUrl; + + /// Whether to delete head branches when pull requests are merged + bool? deleteBranchOnMerge; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/deployments` + String? deploymentsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/downloads` + String? downloadsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/events` + String? eventsUrl; + + int? forks; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/forks` + String? forksUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}` + String? gitCommitsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}` + String? gitRefsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}` + String? gitTagsUrl; + + /// Whether discussions are enabled. + bool? hasDiscussions; + + /// Whether projects are enabled. + bool? hasProjects; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/hooks` + String? hooksUrl; + + /// Whether this repository acts as a template that can be used to generate new + /// repositories. + bool? isTemplate; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}` + String? issueCommentUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/issues/events{/number}` + String? issueEventsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/issues{/number}` + String? issuesUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/keys{/key_id}` + String? keysUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/labels{/name}` + String? labelsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/languages` + String? languagesUrl; + + String? masterBranch; + + /// The default value for a merge commit message. + /// + /// - `PR_TITLE` - default to the pull request's title. + /// - `PR_BODY` - default to the pull request's body. + /// - `BLANK` - default to a blank commit message. + String? mergeCommitMessage; + + /// The default value for a merge commit title. + /// + /// - `PR_TITLE` - default to the pull request's title. + /// - `MERGE_MESSAGE` - default to the classic title for a merge message (e.g., + /// Merge pull request #123 from branch-name). + String? mergeCommitTitle; + + /// Format: uri + /// + /// Example: `http://api.github.com/repos/octocat/Hello-World/merges` + String? mergesUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/milestones{/number}` + String? milestonesUrl; + + /// Format: uri + /// + /// Example: `git:git.example.com/octocat/Hello-World` + String? mirrorUrl; + + /// Example: `MDEwOlJlcG9zaXRvcnkxMjk2MjY5` + String? nodeId; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}` + String? notificationsUrl; + + int? openIssues; + + User? organization; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/pulls{/number}` + String? pullsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/releases{/id}` + String? releasesUrl; + + /// The default value for a squash merge commit message: + /// + /// - `PR_BODY` - default to the pull request's body. + /// - `COMMIT_MESSAGES` - default to the branch's commit messages. + /// - `BLANK` - default to a blank commit message. + String? squashMergeCommitMessage; + + /// The default value for a squash merge commit title: + /// + /// - `PR_TITLE` - default to the pull request's title. + /// - `COMMIT_OR_PR_TITLE` - default to the commit's title (if only one commit) + /// or the pull request's title (when more than one commit). + String? squashMergeCommitTitle; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/stargazers` + String? stargazersUrl; - @JsonKey(name: 'updated_at') - final DateTime updatedAt; + DateTime? starredAt; - final LicenseKind license; + /// Example: `http://api.github.com/repos/octocat/Hello-World/statuses/{sha}` + String? statusesUrl; - final bool archived; - final bool disabled; + /// Example: `http://api.github.com/repos/octocat/Hello-World/subscribers` + String? subscribersUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/subscription` + String? subscriptionUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/tags` + String? tagsUrl; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/teams` + String? teamsUrl; + + String? tempCloneToken; + + TemplateRepository? templateRepository; + + List? topics; + + /// Example: `http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}` + String? treesUrl; + + /// Example: `https://api.github.com/repos/octocat/Hello-World` + String? url; + + /// The repository visibility: public, private, or internal. + String? visibility; + + int? watchers; + + /// Whether to require contributors to sign off on web-based commits + bool? webCommitSignoffRequired; factory Repository.fromJson(Map input) => _$RepositoryFromJson(input); Map toJson() => _$RepositoryToJson(this); /// Gets the Repository Slug (Full Name). - RepositorySlug slug() => RepositorySlug(owner.login, name); + RepositorySlug slug() => RepositorySlug(owner?.login ?? '', name); @override - String toString() => 'Repository: ${owner.login}/$name'; + String toString() => 'Repository: $owner/$name'; } -@JsonSerializable(createToJson: false) +/// Model class for repository permissions. +@JsonSerializable() +class RepositoryPermissions { + RepositoryPermissions( + {this.admin = false, this.push = false, this.pull = false}); + + /// Administrative Access + bool admin; + + /// Push Access + bool push; + + /// Pull Access + bool pull; + + factory RepositoryPermissions.fromJson(Map json) => + _$RepositoryPermissionsFromJson(json); + + Map toJson() => _$RepositoryPermissionsToJson(this); +} + +@JsonSerializable() class Tag { - final String name; - final CommitInfo commit; + Tag(this.name, this.commit, this.zipUrl, this.tarUrl); + + String name; + CommitInfo commit; @JsonKey(name: 'zipball_url') - final String zipUrl; + String zipUrl; @JsonKey(name: 'tarball_url') - final String tarUrl; + String tarUrl; - Tag(this.name, this.commit, this.zipUrl, this.tarUrl); - - factory Tag.fromJson(Map input) => - input == null ? null : _$TagFromJson(input); + factory Tag.fromJson(Map input) => _$TagFromJson(input); + Map toJson() => _$TagToJson(this); @override String toString() => 'Tag: $name'; } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CommitData { - final String sha; - final GitCommit commit; - final String url; - final String htmlUrl; - final String commentsUrl; - - final CommitDataUser author, committer; - final List> parents; - CommitData(this.sha, this.commit, this.url, this.htmlUrl, this.commentsUrl, this.author, this.committer, this.parents); + String? sha; + GitCommit? commit; + String? url; + String? htmlUrl; + String? commentsUrl; + + CommitDataUser? author, committer; + List>? parents; + factory CommitData.fromJson(Map input) => _$CommitDataFromJson(input); Map toJson() => _$CommitDataToJson(this); @@ -220,11 +521,11 @@ class CommitData { @JsonSerializable() class CommitDataUser { - final String login, type; + CommitDataUser(this.login, this.id, this.type); - final int id; + String? login, type; - CommitDataUser(this.login, this.id, this.type); + int? id; factory CommitDataUser.fromJson(Map input) => _$CommitDataUserFromJson(input); @@ -233,32 +534,32 @@ class CommitDataUser { @JsonSerializable() class CommitInfo { - final String sha; - final GitTree tree; - CommitInfo(this.sha, this.tree); + String? sha; + GitTree? tree; + factory CommitInfo.fromJson(Map input) => _$CommitInfoFromJson(input); Map toJson() => _$CommitInfoToJson(this); } /// User Information -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class UserInformation { + UserInformation(this.login, this.id, this.avatarUrl, this.htmlUrl); + /// Owner Username - final String login; + String login; /// Owner ID - final int id; + int id; /// Avatar Url - final String avatarUrl; + String avatarUrl; /// Url to the user's GitHub Profile - final String htmlUrl; - - UserInformation(this.login, this.id, this.avatarUrl, this.htmlUrl); + String htmlUrl; factory UserInformation.fromJson(Map input) => _$UserInformationFromJson(input); @@ -268,17 +569,14 @@ class UserInformation { /// A Repository Slug @JsonSerializable() class RepositorySlug { + RepositorySlug(this.owner, this.name); + /// Repository Owner String owner; /// Repository Name String name; - RepositorySlug(this.owner, this.name) { - ArgumentError.checkNotNull(owner, 'owner'); - ArgumentError.checkNotNull(name, 'name'); - } - /// Creates a Repository Slug from a full name. factory RepositorySlug.full(String f) { final split = f.split('/'); @@ -293,8 +591,8 @@ class RepositorySlug { String get fullName => '$owner/$name'; @override - bool operator ==(Object obj) => - obj is RepositorySlug && obj.fullName == fullName; + bool operator ==(Object other) => + other is RepositorySlug && other.fullName == fullName; @override int get hashCode => fullName.hashCode; @@ -308,7 +606,7 @@ class RepositorySlug { } /// Model class for a new repository to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateRepository { CreateRepository(this.name, {this.description, @@ -323,40 +621,40 @@ class CreateRepository { this.hasWiki}); /// Repository Name - final String name; + String? name; /// Repository Description - String description; + String? description; /// Repository Homepage - String homepage; + String? homepage; /// If the repository should be private or not. - bool private = false; + bool? private = false; /// If the repository should have issues enabled. - bool hasIssues = true; + bool? hasIssues = true; /// If the repository should have the wiki enabled. - bool hasWiki = true; + bool? hasWiki = true; /// If the repository should have downloads enabled. - bool hasDownloads = true; + bool? hasDownloads = true; /// The Team ID (Only for Creating a Repository for an Organization) @OnlyWhen('Creating a repository for an organization') - int teamId; + int? teamId; /// If GitHub should auto initialize the repository. - bool autoInit = false; + bool? autoInit = false; /// .gitignore template (only when [autoInit] is true) @OnlyWhen('autoInit is true') - String gitignoreTemplate; + String? gitignoreTemplate; /// License template (only when [autoInit] is true) @OnlyWhen('autoInit is true') - String licenseTemplate; + String? licenseTemplate; factory CreateRepository.fromJson(Map input) => _$CreateRepositoryFromJson(input); @@ -366,13 +664,13 @@ class CreateRepository { /// Model class for a branch. @JsonSerializable() class Branch { + Branch(this.name, this.commit); + /// The name of the branch. - final String name; + String? name; /// Commit Information - final CommitData commit; - - Branch(this.name, this.commit); + CommitData? commit; factory Branch.fromJson(Map json) => _$BranchFromJson(json); Map toJson() => _$BranchToJson(this); @@ -380,10 +678,10 @@ class Branch { /// A Breakdown of the Languages a repository uses. class LanguageBreakdown { - final Map _data; - LanguageBreakdown(Map data) : _data = data; + final Map _data; + /// The Primary Language String get primary { final list = mapToList(_data); @@ -420,27 +718,8 @@ class LanguageBreakdown { } } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class LicenseDetails { - final String name; - final String path; - final String sha; - final int size; - final Uri url; - - final Uri htmlUrl; - final Uri gitUrl; - final Uri downloadUrl; - - final String type; - final String content; - final String encoding; - - @JsonKey(name: '_links') - final Links links; - - final LicenseKind license; - LicenseDetails( {this.name, this.path, @@ -456,22 +735,41 @@ class LicenseDetails { this.links, this.license}); + String? name; + String? path; + String? sha; + int? size; + Uri? url; + + Uri? htmlUrl; + Uri? gitUrl; + Uri? downloadUrl; + + String? type; + String? content; + String? encoding; + + @JsonKey(name: '_links') + Links? links; + + LicenseKind? license; + factory LicenseDetails.fromJson(Map json) => _$LicenseDetailsFromJson(json); Map toJson() => _$LicenseDetailsToJson(this); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class LicenseKind { - final String key; - final String name; - final String spdxId; - final Uri url; - final String nodeId; - LicenseKind({this.key, this.name, this.spdxId, this.url, this.nodeId}); + String? key; + String? name; + String? spdxId; + Uri? url; + String? nodeId; + factory LicenseKind.fromJson(Map json) => _$LicenseKindFromJson(json); diff --git a/lib/src/common/model/repos.g.dart b/lib/src/common/model/repos.g.dart index 615d58f0..fe19ea97 100644 --- a/lib/src/common/model/repos.g.dart +++ b/lib/src/common/model/repos.g.dart @@ -6,61 +6,153 @@ part of 'repos.dart'; // JsonSerializableGenerator // ************************************************************************** -GitHubComparison _$GitHubComparisonFromJson(Map json) { - return GitHubComparison( - json['url'] as String, - json['status'] as String, - json['ahead_by'] as int, - json['behind_by'] as int, - json['total_commits'] as int, - ); -} +GitHubComparison _$GitHubComparisonFromJson(Map json) => + GitHubComparison( + json['url'] as String?, + json['status'] as String?, + (json['ahead_by'] as num?)?.toInt(), + (json['behind_by'] as num?)?.toInt(), + (json['total_commits'] as num?)?.toInt(), + (json['files'] as List?) + ?.map((e) => CommitFile.fromJson(e as Map)) + .toList(), + (json['commits'] as List?) + ?.map((e) => RepositoryCommit.fromJson(e as Map)) + .toList(), + ); -Repository _$RepositoryFromJson(Map json) { - return Repository( - name: json['name'] as String, - id: json['id'] as int, - fullName: json['full_name'] as String, - owner: json['owner'] == null - ? null - : UserInformation.fromJson(json['owner'] as Map), - isPrivate: json['private'] as bool, - isFork: json['fork'] as bool, - htmlUrl: json['html_url'] as String, - description: json['description'] as String, - cloneUrl: json['clone_url'] as String, - gitUrl: json['git_url'] as String, - sshUrl: json['ssh_url'] as String, - svnUrl: json['svn_url'] as String, - homepage: json['homepage'] as String, - size: json['size'] as int, - stargazersCount: json['stargazers_count'] as int, - watchersCount: json['watchers_count'] as int, - language: json['language'] as String, - hasIssues: json['has_issues'] as bool, - hasWiki: json['has_wiki'] as bool, - hasDownloads: json['has_downloads'] as bool, - forksCount: json['forks_count'] as int, - openIssuesCount: json['open_issues_count'] as int, - defaultBranch: json['default_branch'] as String, - subscribersCount: json['subscribers_count'] as int, - networkCount: json['network_count'] as int, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - pushedAt: json['pushed_at'] == null - ? null - : DateTime.parse(json['pushed_at'] as String), - license: json['license'] == null - ? null - : LicenseKind.fromJson(json['license'] as Map), - archived: json['archived'] as bool, - disabled: json['disabled'] as bool, - ); -} +Map _$GitHubComparisonToJson(GitHubComparison instance) => + { + 'url': instance.url, + 'status': instance.status, + 'ahead_by': instance.aheadBy, + 'behind_by': instance.behindBy, + 'total_commits': instance.totalCommits, + 'files': instance.files, + 'commits': instance.commits, + }; + +Repository _$RepositoryFromJson(Map json) => Repository( + name: json['name'] as String? ?? '', + id: (json['id'] as num?)?.toInt() ?? 0, + fullName: json['full_name'] as String? ?? '', + owner: json['owner'] == null + ? null + : UserInformation.fromJson(json['owner'] as Map), + htmlUrl: json['html_url'] as String? ?? '', + description: json['description'] as String? ?? '', + cloneUrl: json['clone_url'] as String? ?? '', + gitUrl: json['git_url'] as String? ?? '', + sshUrl: json['ssh_url'] as String? ?? '', + svnUrl: json['svn_url'] as String? ?? '', + defaultBranch: json['default_branch'] as String? ?? '', + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + isPrivate: json['private'] as bool? ?? false, + isFork: json['fork'] as bool? ?? false, + stargazersCount: (json['stargazers_count'] as num?)?.toInt() ?? 0, + watchersCount: (json['watchers_count'] as num?)?.toInt() ?? 0, + language: json['language'] as String? ?? '', + hasWiki: json['has_wiki'] as bool? ?? false, + hasDownloads: json['has_downloads'] as bool? ?? false, + forksCount: (json['forks_count'] as num?)?.toInt() ?? 0, + openIssuesCount: (json['open_issues_count'] as num?)?.toInt() ?? 0, + subscribersCount: (json['subscribers_count'] as num?)?.toInt() ?? 0, + networkCount: (json['network_count'] as num?)?.toInt() ?? 0, + hasIssues: json['has_issues'] as bool? ?? false, + size: (json['size'] as num?)?.toInt() ?? 0, + archived: json['archived'] as bool? ?? false, + disabled: json['disabled'] as bool? ?? false, + homepage: json['homepage'] as String? ?? '', + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + pushedAt: json['pushed_at'] == null + ? null + : DateTime.parse(json['pushed_at'] as String), + license: json['license'] == null + ? null + : LicenseKind.fromJson(json['license'] as Map), + hasPages: json['has_pages'] as bool? ?? false, + permissions: json['permissions'] == null + ? null + : RepositoryPermissions.fromJson( + json['permissions'] as Map), + allowAutoMerge: json['allow_auto_merge'] as bool?, + allowForking: json['allow_forking'] as bool?, + allowMergeCommit: json['allow_merge_commit'] as bool?, + allowRebaseMerge: json['allow_rebase_merge'] as bool?, + allowSquashMerge: json['allow_squash_merge'] as bool?, + allowUpdateBranch: json['allow_update_branch'] as bool?, + anonymousAccessEnabled: json['anonymous_access_enabled'] as bool?, + archiveUrl: json['archive_url'] as String?, + assigneesUrl: json['assignees_url'] as String?, + blobsUrl: json['blobs_url'] as String?, + branchesUrl: json['branches_url'] as String?, + collaboratorsUrl: json['collaborators_url'] as String?, + commentsUrl: json['comments_url'] as String?, + commitsUrl: json['commits_url'] as String?, + compareUrl: json['compare_url'] as String?, + contentsUrl: json['contents_url'] as String?, + contributorsUrl: json['contributors_url'] as String?, + deleteBranchOnMerge: json['delete_branch_on_merge'] as bool?, + deploymentsUrl: json['deployments_url'] as String?, + downloadsUrl: json['downloads_url'] as String?, + eventsUrl: json['events_url'] as String?, + forks: (json['forks'] as num?)?.toInt(), + forksUrl: json['forks_url'] as String?, + gitCommitsUrl: json['git_commits_url'] as String?, + gitRefsUrl: json['git_refs_url'] as String?, + gitTagsUrl: json['git_tags_url'] as String?, + hasDiscussions: json['has_discussions'] as bool?, + hasProjects: json['has_projects'] as bool?, + hooksUrl: json['hooks_url'] as String?, + isTemplate: json['is_template'] as bool?, + issueCommentUrl: json['issue_comment_url'] as String?, + issueEventsUrl: json['issue_events_url'] as String?, + issuesUrl: json['issues_url'] as String?, + keysUrl: json['keys_url'] as String?, + labelsUrl: json['labels_url'] as String?, + languagesUrl: json['languages_url'] as String?, + masterBranch: json['master_branch'] as String?, + mergeCommitMessage: json['merge_commit_message'] as String?, + mergeCommitTitle: json['merge_commit_title'] as String?, + mergesUrl: json['merges_url'] as String?, + milestonesUrl: json['milestones_url'] as String?, + mirrorUrl: json['mirror_url'] as String?, + nodeId: json['node_id'] as String?, + notificationsUrl: json['notifications_url'] as String?, + openIssues: (json['open_issues'] as num?)?.toInt(), + organization: json['organization'] == null + ? null + : User.fromJson(json['organization'] as Map), + pullsUrl: json['pulls_url'] as String?, + releasesUrl: json['releases_url'] as String?, + squashMergeCommitMessage: json['squash_merge_commit_message'] as String?, + squashMergeCommitTitle: json['squash_merge_commit_title'] as String?, + stargazersUrl: json['stargazers_url'] as String?, + starredAt: json['starred_at'] == null + ? null + : DateTime.parse(json['starred_at'] as String), + statusesUrl: json['statuses_url'] as String?, + subscribersUrl: json['subscribers_url'] as String?, + subscriptionUrl: json['subscription_url'] as String?, + tagsUrl: json['tags_url'] as String?, + teamsUrl: json['teams_url'] as String?, + tempCloneToken: json['temp_clone_token'] as String?, + templateRepository: json['template_repository'] == null + ? null + : TemplateRepository.fromJson( + json['template_repository'] as Map), + topics: + (json['topics'] as List?)?.map((e) => e as String).toList(), + treesUrl: json['trees_url'] as String?, + url: json['url'] as String?, + visibility: json['visibility'] as String?, + watchers: (json['watchers'] as num?)?.toInt(), + webCommitSignoffRequired: json['web_commit_signoff_required'] as bool?, + ); Map _$RepositoryToJson(Repository instance) => { @@ -84,6 +176,7 @@ Map _$RepositoryToJson(Repository instance) => 'has_issues': instance.hasIssues, 'has_wiki': instance.hasWiki, 'has_downloads': instance.hasDownloads, + 'has_pages': instance.hasPages, 'forks_count': instance.forksCount, 'open_issues_count': instance.openIssuesCount, 'default_branch': instance.defaultBranch, @@ -95,37 +188,122 @@ Map _$RepositoryToJson(Repository instance) => 'license': instance.license, 'archived': instance.archived, 'disabled': instance.disabled, + 'permissions': instance.permissions, + 'allow_auto_merge': instance.allowAutoMerge, + 'allow_forking': instance.allowForking, + 'allow_merge_commit': instance.allowMergeCommit, + 'allow_rebase_merge': instance.allowRebaseMerge, + 'allow_squash_merge': instance.allowSquashMerge, + 'allow_update_branch': instance.allowUpdateBranch, + 'anonymous_access_enabled': instance.anonymousAccessEnabled, + 'archive_url': instance.archiveUrl, + 'assignees_url': instance.assigneesUrl, + 'blobs_url': instance.blobsUrl, + 'branches_url': instance.branchesUrl, + 'collaborators_url': instance.collaboratorsUrl, + 'comments_url': instance.commentsUrl, + 'commits_url': instance.commitsUrl, + 'compare_url': instance.compareUrl, + 'contents_url': instance.contentsUrl, + 'contributors_url': instance.contributorsUrl, + 'delete_branch_on_merge': instance.deleteBranchOnMerge, + 'deployments_url': instance.deploymentsUrl, + 'downloads_url': instance.downloadsUrl, + 'events_url': instance.eventsUrl, + 'forks': instance.forks, + 'forks_url': instance.forksUrl, + 'git_commits_url': instance.gitCommitsUrl, + 'git_refs_url': instance.gitRefsUrl, + 'git_tags_url': instance.gitTagsUrl, + 'has_discussions': instance.hasDiscussions, + 'has_projects': instance.hasProjects, + 'hooks_url': instance.hooksUrl, + 'is_template': instance.isTemplate, + 'issue_comment_url': instance.issueCommentUrl, + 'issue_events_url': instance.issueEventsUrl, + 'issues_url': instance.issuesUrl, + 'keys_url': instance.keysUrl, + 'labels_url': instance.labelsUrl, + 'languages_url': instance.languagesUrl, + 'master_branch': instance.masterBranch, + 'merge_commit_message': instance.mergeCommitMessage, + 'merge_commit_title': instance.mergeCommitTitle, + 'merges_url': instance.mergesUrl, + 'milestones_url': instance.milestonesUrl, + 'mirror_url': instance.mirrorUrl, + 'node_id': instance.nodeId, + 'notifications_url': instance.notificationsUrl, + 'open_issues': instance.openIssues, + 'organization': instance.organization, + 'pulls_url': instance.pullsUrl, + 'releases_url': instance.releasesUrl, + 'squash_merge_commit_message': instance.squashMergeCommitMessage, + 'squash_merge_commit_title': instance.squashMergeCommitTitle, + 'stargazers_url': instance.stargazersUrl, + 'starred_at': instance.starredAt?.toIso8601String(), + 'statuses_url': instance.statusesUrl, + 'subscribers_url': instance.subscribersUrl, + 'subscription_url': instance.subscriptionUrl, + 'tags_url': instance.tagsUrl, + 'teams_url': instance.teamsUrl, + 'temp_clone_token': instance.tempCloneToken, + 'template_repository': instance.templateRepository, + 'topics': instance.topics, + 'trees_url': instance.treesUrl, + 'url': instance.url, + 'visibility': instance.visibility, + 'watchers': instance.watchers, + 'web_commit_signoff_required': instance.webCommitSignoffRequired, }; -Tag _$TagFromJson(Map json) { - return Tag( - json['name'] as String, - json['commit'] == null - ? null - : CommitInfo.fromJson(json['commit'] as Map), - json['zipball_url'] as String, - json['tarball_url'] as String, - ); -} +RepositoryPermissions _$RepositoryPermissionsFromJson( + Map json) => + RepositoryPermissions( + admin: json['admin'] as bool? ?? false, + push: json['push'] as bool? ?? false, + pull: json['pull'] as bool? ?? false, + ); + +Map _$RepositoryPermissionsToJson( + RepositoryPermissions instance) => + { + 'admin': instance.admin, + 'push': instance.push, + 'pull': instance.pull, + }; + +Tag _$TagFromJson(Map json) => Tag( + json['name'] as String, + CommitInfo.fromJson(json['commit'] as Map), + json['zipball_url'] as String, + json['tarball_url'] as String, + ); + +Map _$TagToJson(Tag instance) => { + 'name': instance.name, + 'commit': instance.commit, + 'zipball_url': instance.zipUrl, + 'tarball_url': instance.tarUrl, + }; -CommitData _$CommitDataFromJson(Map json) { - return CommitData( - json['sha'] as String, - json['commit'] == null - ? null - : GitCommit.fromJson(json['commit'] as Map), - json['url'] as String, - json['html_url'] as String, - json['comments_url'] as String, - json['author'] == null - ? null - : CommitDataUser.fromJson(json['author'] as Map), - json['committer'] == null - ? null - : CommitDataUser.fromJson(json['committer'] as Map), - (json['parents'] as List)?.map((e) => e as Map)?.toList(), - ); -} +CommitData _$CommitDataFromJson(Map json) => CommitData( + json['sha'] as String?, + json['commit'] == null + ? null + : GitCommit.fromJson(json['commit'] as Map), + json['url'] as String?, + json['html_url'] as String?, + json['comments_url'] as String?, + json['author'] == null + ? null + : CommitDataUser.fromJson(json['author'] as Map), + json['committer'] == null + ? null + : CommitDataUser.fromJson(json['committer'] as Map), + (json['parents'] as List?) + ?.map((e) => e as Map) + .toList(), + ); Map _$CommitDataToJson(CommitData instance) => { @@ -139,13 +317,12 @@ Map _$CommitDataToJson(CommitData instance) => 'parents': instance.parents, }; -CommitDataUser _$CommitDataUserFromJson(Map json) { - return CommitDataUser( - json['login'] as String, - json['id'] as int, - json['type'] as String, - ); -} +CommitDataUser _$CommitDataUserFromJson(Map json) => + CommitDataUser( + json['login'] as String?, + (json['id'] as num?)?.toInt(), + json['type'] as String?, + ); Map _$CommitDataUserToJson(CommitDataUser instance) => { @@ -154,14 +331,12 @@ Map _$CommitDataUserToJson(CommitDataUser instance) => 'id': instance.id, }; -CommitInfo _$CommitInfoFromJson(Map json) { - return CommitInfo( - json['sha'] as String, - json['tree'] == null - ? null - : GitTree.fromJson(json['tree'] as Map), - ); -} +CommitInfo _$CommitInfoFromJson(Map json) => CommitInfo( + json['sha'] as String?, + json['tree'] == null + ? null + : GitTree.fromJson(json['tree'] as Map), + ); Map _$CommitInfoToJson(CommitInfo instance) => { @@ -169,14 +344,13 @@ Map _$CommitInfoToJson(CommitInfo instance) => 'tree': instance.tree, }; -UserInformation _$UserInformationFromJson(Map json) { - return UserInformation( - json['login'] as String, - json['id'] as int, - json['avatar_url'] as String, - json['html_url'] as String, - ); -} +UserInformation _$UserInformationFromJson(Map json) => + UserInformation( + json['login'] as String, + (json['id'] as num).toInt(), + json['avatar_url'] as String, + json['html_url'] as String, + ); Map _$UserInformationToJson(UserInformation instance) => { @@ -186,12 +360,11 @@ Map _$UserInformationToJson(UserInformation instance) => 'html_url': instance.htmlUrl, }; -RepositorySlug _$RepositorySlugFromJson(Map json) { - return RepositorySlug( - json['owner'] as String, - json['name'] as String, - ); -} +RepositorySlug _$RepositorySlugFromJson(Map json) => + RepositorySlug( + json['owner'] as String, + json['name'] as String, + ); Map _$RepositorySlugToJson(RepositorySlug instance) => { @@ -199,21 +372,20 @@ Map _$RepositorySlugToJson(RepositorySlug instance) => 'name': instance.name, }; -CreateRepository _$CreateRepositoryFromJson(Map json) { - return CreateRepository( - json['name'] as String, - description: json['description'] as String, - homepage: json['homepage'] as String, - private: json['private'] as bool, - hasIssues: json['has_issues'] as bool, - hasDownloads: json['has_downloads'] as bool, - teamId: json['team_id'] as int, - autoInit: json['auto_init'] as bool, - gitignoreTemplate: json['gitignore_template'] as String, - licenseTemplate: json['license_template'] as String, - hasWiki: json['has_wiki'] as bool, - ); -} +CreateRepository _$CreateRepositoryFromJson(Map json) => + CreateRepository( + json['name'] as String?, + description: json['description'] as String?, + homepage: json['homepage'] as String?, + private: json['private'] as bool?, + hasIssues: json['has_issues'] as bool?, + hasDownloads: json['has_downloads'] as bool?, + teamId: (json['team_id'] as num?)?.toInt(), + autoInit: json['auto_init'] as bool?, + gitignoreTemplate: json['gitignore_template'] as String?, + licenseTemplate: json['license_template'] as String?, + hasWiki: json['has_wiki'] as bool?, + ); Map _$CreateRepositoryToJson(CreateRepository instance) => { @@ -230,45 +402,43 @@ Map _$CreateRepositoryToJson(CreateRepository instance) => 'license_template': instance.licenseTemplate, }; -Branch _$BranchFromJson(Map json) { - return Branch( - json['name'] as String, - json['commit'] == null - ? null - : CommitData.fromJson(json['commit'] as Map), - ); -} +Branch _$BranchFromJson(Map json) => Branch( + json['name'] as String?, + json['commit'] == null + ? null + : CommitData.fromJson(json['commit'] as Map), + ); Map _$BranchToJson(Branch instance) => { 'name': instance.name, 'commit': instance.commit, }; -LicenseDetails _$LicenseDetailsFromJson(Map json) { - return LicenseDetails( - name: json['name'] as String, - path: json['path'] as String, - sha: json['sha'] as String, - size: json['size'] as int, - url: json['url'] == null ? null : Uri.parse(json['url'] as String), - htmlUrl: - json['html_url'] == null ? null : Uri.parse(json['html_url'] as String), - gitUrl: - json['git_url'] == null ? null : Uri.parse(json['git_url'] as String), - downloadUrl: json['download_url'] == null - ? null - : Uri.parse(json['download_url'] as String), - type: json['type'] as String, - content: json['content'] as String, - encoding: json['encoding'] as String, - links: json['_links'] == null - ? null - : Links.fromJson(json['_links'] as Map), - license: json['license'] == null - ? null - : LicenseKind.fromJson(json['license'] as Map), - ); -} +LicenseDetails _$LicenseDetailsFromJson(Map json) => + LicenseDetails( + name: json['name'] as String?, + path: json['path'] as String?, + sha: json['sha'] as String?, + size: (json['size'] as num?)?.toInt(), + url: json['url'] == null ? null : Uri.parse(json['url'] as String), + htmlUrl: json['html_url'] == null + ? null + : Uri.parse(json['html_url'] as String), + gitUrl: + json['git_url'] == null ? null : Uri.parse(json['git_url'] as String), + downloadUrl: json['download_url'] == null + ? null + : Uri.parse(json['download_url'] as String), + type: json['type'] as String?, + content: json['content'] as String?, + encoding: json['encoding'] as String?, + links: json['_links'] == null + ? null + : Links.fromJson(json['_links'] as Map), + license: json['license'] == null + ? null + : LicenseKind.fromJson(json['license'] as Map), + ); Map _$LicenseDetailsToJson(LicenseDetails instance) => { @@ -287,15 +457,13 @@ Map _$LicenseDetailsToJson(LicenseDetails instance) => 'license': instance.license, }; -LicenseKind _$LicenseKindFromJson(Map json) { - return LicenseKind( - key: json['key'] as String, - name: json['name'] as String, - spdxId: json['spdx_id'] as String, - url: json['url'] == null ? null : Uri.parse(json['url'] as String), - nodeId: json['node_id'] as String, - ); -} +LicenseKind _$LicenseKindFromJson(Map json) => LicenseKind( + key: json['key'] as String?, + name: json['name'] as String?, + spdxId: json['spdx_id'] as String?, + url: json['url'] == null ? null : Uri.parse(json['url'] as String), + nodeId: json['node_id'] as String?, + ); Map _$LicenseKindToJson(LicenseKind instance) => { diff --git a/lib/src/common/model/repos_commits.dart b/lib/src/common/model/repos_commits.dart index 606132c8..f79c774f 100644 --- a/lib/src/common/model/repos_commits.dart +++ b/lib/src/common/model/repos_commits.dart @@ -1,5 +1,4 @@ import 'package:github/src/common.dart'; -import 'package:github/src/common/model/users.dart'; import 'package:json_annotation/json_annotation.dart'; part 'repos_commits.g.dart'; @@ -25,36 +24,36 @@ class RepositoryCommit { }); /// API url. - String url; + String? url; /// Commit SHA - String sha; + String? sha; /// Url to Commit Page @JsonKey(name: 'html_url') - String htmlUrl; + String? htmlUrl; /// Comments url. @JsonKey(name: 'comments_url') - String commentsUrl; + String? commentsUrl; /// A reference to the raw [GitCommit]. - GitCommit commit; + GitCommit? commit; /// Commit Author - User author; + User? author; /// Commit Committer. - User committer; + User? committer; /// Commit parents. - List parents; + List? parents; /// Commit statistics. - CommitStats stats; + CommitStats? stats; /// The files changed in this commit. - List files; + List? files; factory RepositoryCommit.fromJson(Map input) => _$RepositoryCommitFromJson(input); @@ -71,13 +70,13 @@ class CommitStats { }); /// Number of Additions. - int additions; + int? additions; /// Number of Deletions. - int deletions; + int? deletions; /// Total changes. - int total; + int? total; factory CommitStats.fromJson(Map input) => _$CommitStatsFromJson(input); @@ -98,20 +97,20 @@ class CommitFile { this.patch, }); @JsonKey(name: 'filename') - String name; + String? name; - int additions; - int deletions; - int changes; - String status; + int? additions; + int? deletions; + int? changes; + String? status; @JsonKey(name: 'raw_url') - String rawUrl; + String? rawUrl; @JsonKey(name: 'blob_url') - String blobUrl; + String? blobUrl; - String patch; + String? patch; factory CommitFile.fromJson(Map input) => _$CommitFileFromJson(input); @@ -121,7 +120,7 @@ class CommitFile { /// Model class for a commit comment. /// /// See https://developer.github.com/v3/repos/comments -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CommitComment { CommitComment({ this.id, @@ -134,37 +133,56 @@ class CommitComment { this.htmlUrl, this.updatedAt, this.body, + + // Properties from the Timeline API + this.authorAssociation, + this.nodeId, + this.reactions, + this.user, }); /// Id of the comment - final int id; + int? id; /// Relative path of the file on which the comment has been posted - final String path; + String? path; /// Line on file - final int line; + int? line; /// Position on the diff - final int position; + int? position; /// SHA of the commit where the comment has been made - final String commitId; + String? commitId; - final DateTime createdAt; + DateTime? createdAt; /// Can be equals to [createdAt] - final DateTime updatedAt; + DateTime? updatedAt; /// Ex: https://github.com/... - final String htmlUrl; + String? htmlUrl; /// Ex: https://api.github.com/... @JsonKey(name: 'url') - final String apiUrl; + String? apiUrl; /// Content of the comment - final String body; + String? body; + + // The following properties were added to support the Timeline API. + + /// How the author is associated with the repository. + /// + /// Example: `OWNER` + String? authorAssociation; + + String? nodeId; + + ReactionRollup? reactions; + + User? user; factory CommitComment.fromJson(Map input) => _$CommitCommentFromJson(input); diff --git a/lib/src/common/model/repos_commits.g.dart b/lib/src/common/model/repos_commits.g.dart index 75ebf64d..a4b3a5fe 100644 --- a/lib/src/common/model/repos_commits.g.dart +++ b/lib/src/common/model/repos_commits.g.dart @@ -6,34 +6,31 @@ part of 'repos_commits.dart'; // JsonSerializableGenerator // ************************************************************************** -RepositoryCommit _$RepositoryCommitFromJson(Map json) { - return RepositoryCommit( - url: json['url'] as String, - sha: json['sha'] as String, - htmlUrl: json['html_url'] as String, - commentsUrl: json['comments_url'] as String, - commit: json['commit'] == null - ? null - : GitCommit.fromJson(json['commit'] as Map), - author: json['author'] == null - ? null - : User.fromJson(json['author'] as Map), - committer: json['committer'] == null - ? null - : User.fromJson(json['committer'] as Map), - parents: (json['parents'] as List) - ?.map((e) => - e == null ? null : GitCommit.fromJson(e as Map)) - ?.toList(), - stats: json['stats'] == null - ? null - : CommitStats.fromJson(json['stats'] as Map), - files: (json['files'] as List) - ?.map((e) => - e == null ? null : CommitFile.fromJson(e as Map)) - ?.toList(), - ); -} +RepositoryCommit _$RepositoryCommitFromJson(Map json) => + RepositoryCommit( + url: json['url'] as String?, + sha: json['sha'] as String?, + htmlUrl: json['html_url'] as String?, + commentsUrl: json['comments_url'] as String?, + commit: json['commit'] == null + ? null + : GitCommit.fromJson(json['commit'] as Map), + author: json['author'] == null + ? null + : User.fromJson(json['author'] as Map), + committer: json['committer'] == null + ? null + : User.fromJson(json['committer'] as Map), + parents: (json['parents'] as List?) + ?.map((e) => GitCommit.fromJson(e as Map)) + .toList(), + stats: json['stats'] == null + ? null + : CommitStats.fromJson(json['stats'] as Map), + files: (json['files'] as List?) + ?.map((e) => CommitFile.fromJson(e as Map)) + .toList(), + ); Map _$RepositoryCommitToJson(RepositoryCommit instance) => { @@ -49,13 +46,11 @@ Map _$RepositoryCommitToJson(RepositoryCommit instance) => 'files': instance.files, }; -CommitStats _$CommitStatsFromJson(Map json) { - return CommitStats( - additions: json['additions'] as int, - deletions: json['deletions'] as int, - total: json['total'] as int, - ); -} +CommitStats _$CommitStatsFromJson(Map json) => CommitStats( + additions: (json['additions'] as num?)?.toInt(), + deletions: (json['deletions'] as num?)?.toInt(), + total: (json['total'] as num?)?.toInt(), + ); Map _$CommitStatsToJson(CommitStats instance) => { @@ -64,18 +59,16 @@ Map _$CommitStatsToJson(CommitStats instance) => 'total': instance.total, }; -CommitFile _$CommitFileFromJson(Map json) { - return CommitFile( - name: json['filename'] as String, - additions: json['additions'] as int, - deletions: json['deletions'] as int, - changes: json['changes'] as int, - status: json['status'] as String, - rawUrl: json['raw_url'] as String, - blobUrl: json['blob_url'] as String, - patch: json['patch'] as String, - ); -} +CommitFile _$CommitFileFromJson(Map json) => CommitFile( + name: json['filename'] as String?, + additions: (json['additions'] as num?)?.toInt(), + deletions: (json['deletions'] as num?)?.toInt(), + changes: (json['changes'] as num?)?.toInt(), + status: json['status'] as String?, + rawUrl: json['raw_url'] as String?, + blobUrl: json['blob_url'] as String?, + patch: json['patch'] as String?, + ); Map _$CommitFileToJson(CommitFile instance) => { @@ -89,24 +82,31 @@ Map _$CommitFileToJson(CommitFile instance) => 'patch': instance.patch, }; -CommitComment _$CommitCommentFromJson(Map json) { - return CommitComment( - id: json['id'] as int, - line: json['line'] as int, - position: json['position'] as int, - path: json['path'] as String, - apiUrl: json['url'] as String, - commitId: json['commit_id'] as String, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - htmlUrl: json['html_url'] as String, - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - body: json['body'] as String, - ); -} +CommitComment _$CommitCommentFromJson(Map json) => + CommitComment( + id: (json['id'] as num?)?.toInt(), + line: (json['line'] as num?)?.toInt(), + position: (json['position'] as num?)?.toInt(), + path: json['path'] as String?, + apiUrl: json['url'] as String?, + commitId: json['commit_id'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + htmlUrl: json['html_url'] as String?, + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + body: json['body'] as String?, + authorAssociation: json['author_association'] as String?, + nodeId: json['node_id'] as String?, + reactions: json['reactions'] == null + ? null + : ReactionRollup.fromJson(json['reactions'] as Map), + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + ); Map _$CommitCommentToJson(CommitComment instance) => { @@ -120,4 +120,8 @@ Map _$CommitCommentToJson(CommitComment instance) => 'html_url': instance.htmlUrl, 'url': instance.apiUrl, 'body': instance.body, + 'author_association': instance.authorAssociation, + 'node_id': instance.nodeId, + 'reactions': instance.reactions, + 'user': instance.user, }; diff --git a/lib/src/common/model/repos_contents.dart b/lib/src/common/model/repos_contents.dart index 6ecc335b..53250994 100644 --- a/lib/src/common/model/repos_contents.dart +++ b/lib/src/common/model/repos_contents.dart @@ -24,52 +24,52 @@ class GitHubFile { }); /// Type of File - String type; + String? type; /// File Encoding - String encoding; + String? encoding; /// File Size - int size; + int? size; /// File Name - String name; + String? name; /// File Path - String path; + String? path; /// Base-64 encoded file content with newlines. - String content; + String? content; /// SHA - String sha; + String? sha; /// Url to file @JsonKey(name: 'html_url') - String htmlUrl; + String? htmlUrl; /// Git Url @JsonKey(name: 'git_url') - String gitUrl; + String? gitUrl; /// Download Url @JsonKey(name: 'download_url') - String downloadUrl; + String? downloadUrl; /// Links @JsonKey(name: '_links') - Links links; + Links? links; /// The value in [content] Base-64 decoded. String get text { return _text ??= - utf8.decode(base64Decode(LineSplitter.split(content).join())); + utf8.decode(base64Decode(LineSplitter.split(content!).join())); } - String _text; + String? _text; /// Source Repository - RepositorySlug sourceRepository; + RepositorySlug? sourceRepository; factory GitHubFile.fromJson(Map input) => _$GitHubFileFromJson(input); @@ -78,9 +78,9 @@ class GitHubFile { @JsonSerializable() class Links { - final Uri self; - final Uri git; - final Uri html; + final Uri? self; + final Uri? git; + final Uri? html; Links({this.git, this.self, this.html}); @@ -90,14 +90,14 @@ class Links { } /// Model class for a file or directory. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class RepositoryContents { RepositoryContents({ this.file, this.tree, }); - GitHubFile file; - List tree; + GitHubFile? file; + List? tree; bool get isFile => file != null; bool get isDirectory => tree != null; @@ -110,16 +110,16 @@ class RepositoryContents { /// Model class for a new file to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateFile { CreateFile( {this.path, this.content, this.message, this.branch, this.committer}); - String path; - String message; - String content; - String branch; - CommitUser committer; + String? path; + String? message; + String? content; + String? branch; + CommitUser? committer; factory CreateFile.fromJson(Map json) => _$CreateFileFromJson(json); @@ -128,12 +128,12 @@ class CreateFile { } /// Model class for a committer of a commit. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CommitUser { CommitUser(this.name, this.email); - final String name; - final String email; + final String? name; + final String? email; factory CommitUser.fromJson(Map input) => _$CommitUserFromJson(input); @@ -144,8 +144,8 @@ class CommitUser { /// Model class for the response of a content creation. @JsonSerializable() class ContentCreation { - final RepositoryCommit commit; - final GitHubFile content; + final RepositoryCommit? commit; + final GitHubFile? content; ContentCreation(this.commit, this.content); diff --git a/lib/src/common/model/repos_contents.g.dart b/lib/src/common/model/repos_contents.g.dart index afa3a385..84dc677b 100644 --- a/lib/src/common/model/repos_contents.g.dart +++ b/lib/src/common/model/repos_contents.g.dart @@ -6,27 +6,25 @@ part of 'repos_contents.dart'; // JsonSerializableGenerator // ************************************************************************** -GitHubFile _$GitHubFileFromJson(Map json) { - return GitHubFile( - type: json['type'] as String, - encoding: json['encoding'] as String, - size: json['size'] as int, - name: json['name'] as String, - path: json['path'] as String, - content: json['content'] as String, - sha: json['sha'] as String, - htmlUrl: json['html_url'] as String, - gitUrl: json['git_url'] as String, - downloadUrl: json['download_url'] as String, - links: json['_links'] == null - ? null - : Links.fromJson(json['_links'] as Map), - sourceRepository: json['sourceRepository'] == null - ? null - : RepositorySlug.fromJson( - json['sourceRepository'] as Map), - ); -} +GitHubFile _$GitHubFileFromJson(Map json) => GitHubFile( + type: json['type'] as String?, + encoding: json['encoding'] as String?, + size: (json['size'] as num?)?.toInt(), + name: json['name'] as String?, + path: json['path'] as String?, + content: json['content'] as String?, + sha: json['sha'] as String?, + htmlUrl: json['html_url'] as String?, + gitUrl: json['git_url'] as String?, + downloadUrl: json['download_url'] as String?, + links: json['_links'] == null + ? null + : Links.fromJson(json['_links'] as Map), + sourceRepository: json['source_repository'] == null + ? null + : RepositorySlug.fromJson( + json['source_repository'] as Map), + ); Map _$GitHubFileToJson(GitHubFile instance) => { @@ -41,16 +39,14 @@ Map _$GitHubFileToJson(GitHubFile instance) => 'git_url': instance.gitUrl, 'download_url': instance.downloadUrl, '_links': instance.links, - 'sourceRepository': instance.sourceRepository, + 'source_repository': instance.sourceRepository, }; -Links _$LinksFromJson(Map json) { - return Links( - git: json['git'] == null ? null : Uri.parse(json['git'] as String), - self: json['self'] == null ? null : Uri.parse(json['self'] as String), - html: json['html'] == null ? null : Uri.parse(json['html'] as String), - ); -} +Links _$LinksFromJson(Map json) => Links( + git: json['git'] == null ? null : Uri.parse(json['git'] as String), + self: json['self'] == null ? null : Uri.parse(json['self'] as String), + html: json['html'] == null ? null : Uri.parse(json['html'] as String), + ); Map _$LinksToJson(Links instance) => { 'self': instance.self?.toString(), @@ -58,17 +54,15 @@ Map _$LinksToJson(Links instance) => { 'html': instance.html?.toString(), }; -RepositoryContents _$RepositoryContentsFromJson(Map json) { - return RepositoryContents( - file: json['file'] == null - ? null - : GitHubFile.fromJson(json['file'] as Map), - tree: (json['tree'] as List) - ?.map((e) => - e == null ? null : GitHubFile.fromJson(e as Map)) - ?.toList(), - ); -} +RepositoryContents _$RepositoryContentsFromJson(Map json) => + RepositoryContents( + file: json['file'] == null + ? null + : GitHubFile.fromJson(json['file'] as Map), + tree: (json['tree'] as List?) + ?.map((e) => GitHubFile.fromJson(e as Map)) + .toList(), + ); Map _$RepositoryContentsToJson(RepositoryContents instance) => { @@ -76,17 +70,15 @@ Map _$RepositoryContentsToJson(RepositoryContents instance) => 'tree': instance.tree, }; -CreateFile _$CreateFileFromJson(Map json) { - return CreateFile( - path: json['path'] as String, - content: json['content'] as String, - message: json['message'] as String, - branch: json['branch'] as String, - committer: json['committer'] == null - ? null - : CommitUser.fromJson(json['committer'] as Map), - ); -} +CreateFile _$CreateFileFromJson(Map json) => CreateFile( + path: json['path'] as String?, + content: json['content'] as String?, + message: json['message'] as String?, + branch: json['branch'] as String?, + committer: json['committer'] == null + ? null + : CommitUser.fromJson(json['committer'] as Map), + ); Map _$CreateFileToJson(CreateFile instance) => { @@ -97,12 +89,10 @@ Map _$CreateFileToJson(CreateFile instance) => 'committer': instance.committer, }; -CommitUser _$CommitUserFromJson(Map json) { - return CommitUser( - json['name'] as String, - json['email'] as String, - ); -} +CommitUser _$CommitUserFromJson(Map json) => CommitUser( + json['name'] as String?, + json['email'] as String?, + ); Map _$CommitUserToJson(CommitUser instance) => { @@ -110,16 +100,15 @@ Map _$CommitUserToJson(CommitUser instance) => 'email': instance.email, }; -ContentCreation _$ContentCreationFromJson(Map json) { - return ContentCreation( - json['commit'] == null - ? null - : RepositoryCommit.fromJson(json['commit'] as Map), - json['content'] == null - ? null - : GitHubFile.fromJson(json['content'] as Map), - ); -} +ContentCreation _$ContentCreationFromJson(Map json) => + ContentCreation( + json['commit'] == null + ? null + : RepositoryCommit.fromJson(json['commit'] as Map), + json['content'] == null + ? null + : GitHubFile.fromJson(json['content'] as Map), + ); Map _$ContentCreationToJson(ContentCreation instance) => { diff --git a/lib/src/common/model/repos_forks.dart b/lib/src/common/model/repos_forks.dart index 6830130d..cbf4b68c 100644 --- a/lib/src/common/model/repos_forks.dart +++ b/lib/src/common/model/repos_forks.dart @@ -2,11 +2,11 @@ import 'package:json_annotation/json_annotation.dart'; part 'repos_forks.g.dart'; /// Model class for a new fork to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateFork { CreateFork([this.organization]); - String organization; + String? organization; factory CreateFork.fromJson(Map input) => _$CreateForkFromJson(input); diff --git a/lib/src/common/model/repos_forks.g.dart b/lib/src/common/model/repos_forks.g.dart index 55779541..0d41f4e9 100644 --- a/lib/src/common/model/repos_forks.g.dart +++ b/lib/src/common/model/repos_forks.g.dart @@ -6,11 +6,9 @@ part of 'repos_forks.dart'; // JsonSerializableGenerator // ************************************************************************** -CreateFork _$CreateForkFromJson(Map json) { - return CreateFork( - json['organization'] as String, - ); -} +CreateFork _$CreateForkFromJson(Map json) => CreateFork( + json['organization'] as String?, + ); Map _$CreateForkToJson(CreateFork instance) => { diff --git a/lib/src/common/model/repos_hooks.dart b/lib/src/common/model/repos_hooks.dart index 0c181ed5..f39972a1 100644 --- a/lib/src/common/model/repos_hooks.dart +++ b/lib/src/common/model/repos_hooks.dart @@ -3,41 +3,41 @@ import 'package:json_annotation/json_annotation.dart'; part 'repos_hooks.g.dart'; /// Model class for a repository hook. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class Hook { Hook({ this.id, this.name, }); - int id; - String name; + int? id; + String? name; /// Events to Subscribe to - List events; + List? events; /// Content Type - String get contentType => config.contentType; + String? get contentType => config!.contentType; /// If the hook is active - bool active; + bool? active; /// The time the hook was created - DateTime createdAt; + DateTime? createdAt; /// The last time the hook was updated - DateTime updatedAt; + DateTime? updatedAt; /// The Repository Name - String repoName; + String? repoName; - HookConfig config; + HookConfig? config; factory Hook.fromJson(Map input) => _$HookFromJson(input); Map toJson() => _$HookToJson(this); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class HookConfig { HookConfig({ this.url, @@ -45,29 +45,29 @@ class HookConfig { this.secret, this.insecureSsl, }); - String url; - String contentType; - String secret; - String insecureSsl; + String? url; + String? contentType; + String? secret; + String? insecureSsl; factory HookConfig.fromJson(Map input) => _$HookConfigFromJson(input); Map toJson() => _$HookConfigToJson(this); } /// Model class for a new hook to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateHook { /// Hook Name - final String name; + final String? name; /// Hook Configuration - final HookConfig config; + final HookConfig? config; /// Events to Subscribe to - final List events; + final List? events; /// If the Hook should be active. - final bool active; + final bool? active; CreateHook(this.name, this.config, {this.events = const ['push'], this.active = true}); diff --git a/lib/src/common/model/repos_hooks.g.dart b/lib/src/common/model/repos_hooks.g.dart index c74559a0..fa57fa4f 100644 --- a/lib/src/common/model/repos_hooks.g.dart +++ b/lib/src/common/model/repos_hooks.g.dart @@ -6,24 +6,23 @@ part of 'repos_hooks.dart'; // JsonSerializableGenerator // ************************************************************************** -Hook _$HookFromJson(Map json) { - return Hook( - id: json['id'] as int, - name: json['name'] as String, - ) - ..events = (json['events'] as List)?.map((e) => e as String)?.toList() - ..active = json['active'] as bool - ..createdAt = json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String) - ..updatedAt = json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String) - ..repoName = json['repo_name'] as String - ..config = json['config'] == null - ? null - : HookConfig.fromJson(json['config'] as Map); -} +Hook _$HookFromJson(Map json) => Hook( + id: (json['id'] as num?)?.toInt(), + name: json['name'] as String?, + ) + ..events = + (json['events'] as List?)?.map((e) => e as String).toList() + ..active = json['active'] as bool? + ..createdAt = json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String) + ..updatedAt = json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String) + ..repoName = json['repo_name'] as String? + ..config = json['config'] == null + ? null + : HookConfig.fromJson(json['config'] as Map); Map _$HookToJson(Hook instance) => { 'id': instance.id, @@ -36,14 +35,12 @@ Map _$HookToJson(Hook instance) => { 'config': instance.config, }; -HookConfig _$HookConfigFromJson(Map json) { - return HookConfig( - url: json['url'] as String, - contentType: json['content_type'] as String, - secret: json['secret'] as String, - insecureSsl: json['insecure_ssl'] as String, - ); -} +HookConfig _$HookConfigFromJson(Map json) => HookConfig( + url: json['url'] as String?, + contentType: json['content_type'] as String?, + secret: json['secret'] as String?, + insecureSsl: json['insecure_ssl'] as String?, + ); Map _$HookConfigToJson(HookConfig instance) => { @@ -53,16 +50,17 @@ Map _$HookConfigToJson(HookConfig instance) => 'insecure_ssl': instance.insecureSsl, }; -CreateHook _$CreateHookFromJson(Map json) { - return CreateHook( - json['name'] as String, - json['config'] == null - ? null - : HookConfig.fromJson(json['config'] as Map), - events: (json['events'] as List)?.map((e) => e as String)?.toList(), - active: json['active'] as bool, - ); -} +CreateHook _$CreateHookFromJson(Map json) => CreateHook( + json['name'] as String?, + json['config'] == null + ? null + : HookConfig.fromJson(json['config'] as Map), + events: (json['events'] as List?) + ?.map((e) => e as String) + .toList() ?? + const ['push'], + active: json['active'] as bool? ?? true, + ); Map _$CreateHookToJson(CreateHook instance) => { diff --git a/lib/src/common/model/repos_merging.dart b/lib/src/common/model/repos_merging.dart index 28bd2bf7..7deaf0e5 100644 --- a/lib/src/common/model/repos_merging.dart +++ b/lib/src/common/model/repos_merging.dart @@ -2,13 +2,13 @@ import 'package:json_annotation/json_annotation.dart'; part 'repos_merging.g.dart'; /// Model class for a new merge to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateMerge { CreateMerge(this.base, this.head, {this.commitMessage}); - final String base; - final String head; - String commitMessage; + final String? base; + final String? head; + String? commitMessage; factory CreateMerge.fromJson(Map input) => _$CreateMergeFromJson(input); diff --git a/lib/src/common/model/repos_merging.g.dart b/lib/src/common/model/repos_merging.g.dart index ee0b8dc2..be3777c0 100644 --- a/lib/src/common/model/repos_merging.g.dart +++ b/lib/src/common/model/repos_merging.g.dart @@ -6,13 +6,11 @@ part of 'repos_merging.dart'; // JsonSerializableGenerator // ************************************************************************** -CreateMerge _$CreateMergeFromJson(Map json) { - return CreateMerge( - json['base'] as String, - json['head'] as String, - commitMessage: json['commit_message'] as String, - ); -} +CreateMerge _$CreateMergeFromJson(Map json) => CreateMerge( + json['base'] as String?, + json['head'] as String?, + commitMessage: json['commit_message'] as String?, + ); Map _$CreateMergeToJson(CreateMerge instance) => { diff --git a/lib/src/common/model/repos_pages.dart b/lib/src/common/model/repos_pages.dart index 990e6403..09d0bf0b 100644 --- a/lib/src/common/model/repos_pages.dart +++ b/lib/src/common/model/repos_pages.dart @@ -3,7 +3,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'repos_pages.g.dart'; /// GitHub Pages Information -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class RepositoryPages { RepositoryPages({ this.cname, @@ -11,17 +11,17 @@ class RepositoryPages { this.hasCustom404, }); - String cname; - String status; + String? cname; + String? status; @JsonKey(name: 'custom_404') - bool hasCustom404; + bool? hasCustom404; factory RepositoryPages.fromJson(Map input) => _$RepositoryPagesFromJson(input); Map toJson() => _$RepositoryPagesToJson(this); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class PageBuild { PageBuild({ this.url, @@ -33,21 +33,21 @@ class PageBuild { this.createdAt, this.updatedAt, }); - String url; - String status; - PageBuildError error; - PageBuildPusher pusher; - String commit; - int duration; - DateTime createdAt; - DateTime updatedAt; + String? url; + String? status; + PageBuildError? error; + PageBuildPusher? pusher; + String? commit; + int? duration; + DateTime? createdAt; + DateTime? updatedAt; factory PageBuild.fromJson(Map input) => _$PageBuildFromJson(input); Map toJson() => _$PageBuildToJson(this); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class PageBuildPusher { PageBuildPusher({ this.login, @@ -57,23 +57,23 @@ class PageBuildPusher { this.type, this.siteAdmin, }); - int id; - String login; + int? id; + String? login; @JsonKey(name: 'url') - String apiUrl; - String htmlUrl; - String type; - bool siteAdmin; + String? apiUrl; + String? htmlUrl; + String? type; + bool? siteAdmin; factory PageBuildPusher.fromJson(Map input) => _$PageBuildPusherFromJson(input); Map toJson() => _$PageBuildPusherToJson(this); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class PageBuildError { PageBuildError({this.message}); - String message; + String? message; factory PageBuildError.fromJson(Map input) => _$PageBuildErrorFromJson(input); diff --git a/lib/src/common/model/repos_pages.g.dart b/lib/src/common/model/repos_pages.g.dart index 790ec2ef..ead9a68f 100644 --- a/lib/src/common/model/repos_pages.g.dart +++ b/lib/src/common/model/repos_pages.g.dart @@ -6,13 +6,12 @@ part of 'repos_pages.dart'; // JsonSerializableGenerator // ************************************************************************** -RepositoryPages _$RepositoryPagesFromJson(Map json) { - return RepositoryPages( - cname: json['cname'] as String, - status: json['status'] as String, - hasCustom404: json['custom_404'] as bool, - ); -} +RepositoryPages _$RepositoryPagesFromJson(Map json) => + RepositoryPages( + cname: json['cname'] as String?, + status: json['status'] as String?, + hasCustom404: json['custom_404'] as bool?, + ); Map _$RepositoryPagesToJson(RepositoryPages instance) => { @@ -21,26 +20,24 @@ Map _$RepositoryPagesToJson(RepositoryPages instance) => 'custom_404': instance.hasCustom404, }; -PageBuild _$PageBuildFromJson(Map json) { - return PageBuild( - url: json['url'] as String, - status: json['status'] as String, - error: json['error'] == null - ? null - : PageBuildError.fromJson(json['error'] as Map), - pusher: json['pusher'] == null - ? null - : PageBuildPusher.fromJson(json['pusher'] as Map), - commit: json['commit'] as String, - duration: json['duration'] as int, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - ); -} +PageBuild _$PageBuildFromJson(Map json) => PageBuild( + url: json['url'] as String?, + status: json['status'] as String?, + error: json['error'] == null + ? null + : PageBuildError.fromJson(json['error'] as Map), + pusher: json['pusher'] == null + ? null + : PageBuildPusher.fromJson(json['pusher'] as Map), + commit: json['commit'] as String?, + duration: (json['duration'] as num?)?.toInt(), + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + ); Map _$PageBuildToJson(PageBuild instance) => { 'url': instance.url, @@ -53,16 +50,15 @@ Map _$PageBuildToJson(PageBuild instance) => { 'updated_at': instance.updatedAt?.toIso8601String(), }; -PageBuildPusher _$PageBuildPusherFromJson(Map json) { - return PageBuildPusher( - login: json['login'] as String, - id: json['id'] as int, - apiUrl: json['url'] as String, - htmlUrl: json['html_url'] as String, - type: json['type'] as String, - siteAdmin: json['site_admin'] as bool, - ); -} +PageBuildPusher _$PageBuildPusherFromJson(Map json) => + PageBuildPusher( + login: json['login'] as String?, + id: (json['id'] as num?)?.toInt(), + apiUrl: json['url'] as String?, + htmlUrl: json['html_url'] as String?, + type: json['type'] as String?, + siteAdmin: json['site_admin'] as bool?, + ); Map _$PageBuildPusherToJson(PageBuildPusher instance) => { @@ -74,11 +70,10 @@ Map _$PageBuildPusherToJson(PageBuildPusher instance) => 'site_admin': instance.siteAdmin, }; -PageBuildError _$PageBuildErrorFromJson(Map json) { - return PageBuildError( - message: json['message'] as String, - ); -} +PageBuildError _$PageBuildErrorFromJson(Map json) => + PageBuildError( + message: json['message'] as String?, + ); Map _$PageBuildErrorToJson(PageBuildError instance) => { diff --git a/lib/src/common/model/repos_releases.dart b/lib/src/common/model/repos_releases.dart index 59085fe3..30a80172 100644 --- a/lib/src/common/model/repos_releases.dart +++ b/lib/src/common/model/repos_releases.dart @@ -2,12 +2,11 @@ import 'dart:typed_data'; import 'package:github/src/common/model/users.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'package:meta/meta.dart'; part 'repos_releases.g.dart'; /// Model class for a release. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class Release { Release({ this.id, @@ -30,77 +29,77 @@ class Release { }); /// Url to this Release - String url; + String? url; /// Url to this Release - String htmlUrl; + String? htmlUrl; /// Tarball of the Repository Tree at the commit of this release. - String tarballUrl; + String? tarballUrl; /// ZIP of the Repository Tree at the commit of this release. - String zipballUrl; + String? zipballUrl; /// The endpoint for uploading release assets. /// This key is a hypermedia resource. https://developer.github.com/v3/#hypermedia - String uploadUrl; + String? uploadUrl; - String assetsUrl; + String? assetsUrl; /// Release ID - int id; + int? id; - String nodeId; + String? nodeId; /// Release Tag Name - String tagName; + String? tagName; /// Target Commit - String targetCommitish; + String? targetCommitish; /// Release Name - String name; + String? name; /// Release Notes - String body; + String? body; /// Release Description - String description; + String? description; /// If the release is a draft. @JsonKey(name: 'draft') - bool isDraft; + bool? isDraft; /// If the release is a pre-release. @JsonKey(name: 'prerelease') - bool isPrerelease; + bool? isPrerelease; /// The time this release was created at. - DateTime createdAt; + DateTime? createdAt; /// The time this release was published at. - DateTime publishedAt; + DateTime? publishedAt; /// The author of this release. - User author; + User? author; /// Release Assets - List assets; + List? assets; - List errors; + List? errors; factory Release.fromJson(Map input) => _$ReleaseFromJson(input); Map toJson() => _$ReleaseToJson(this); - String getUploadUrlFor(String name, [String label]) => - "${uploadUrl.substring(0, uploadUrl.indexOf('{'))}?name=$name${label != null ? ",$label" : ""}"; + String getUploadUrlFor(String name, [String? label]) => + "${uploadUrl!.substring(0, uploadUrl!.indexOf('{'))}?name=$name${label != null ? ",$label" : ""}"; - bool get hasErrors => errors == null ? false : errors.isNotEmpty; + bool get hasErrors => errors == null ? false : errors!.isNotEmpty; } /// Model class for a release asset. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class ReleaseAsset { ReleaseAsset({ this.id, @@ -116,34 +115,34 @@ class ReleaseAsset { }); /// Url to download the asset. - String browserDownloadUrl; + String? browserDownloadUrl; /// Asset ID - int id; + int? id; /// Asset Name - String name; + String? name; /// Asset Label - String label; + String? label; /// Asset State - String state; + String? state; /// Asset Content Type - String contentType; + String? contentType; /// Size of Asset - int size; + int? size; /// Number of Downloads - int downloadCount; + int? downloadCount; /// Time the asset was created at - DateTime createdAt; + DateTime? createdAt; /// Time the asset was last updated - DateTime updatedAt; + DateTime? updatedAt; factory ReleaseAsset.fromJson(Map input) => _$ReleaseAssetFromJson(input); @@ -151,39 +150,45 @@ class ReleaseAsset { } /// Model class for a new release to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateRelease { /// Tag Name to Base off of - final String tagName; + final String? tagName; /// Commit to Target - String targetCommitish; + String? targetCommitish; /// Release Name - String name; + String? name; /// Release Body - String body; + String? body; /// If the release is a draft @JsonKey(name: 'draft') - bool isDraft; + bool? isDraft; /// true to identify the release as a prerelease. /// false to identify the release as a full release. Default: false @JsonKey(name: 'prerelease') - bool isPrerelease; + bool? isPrerelease; + + String? discussionCategoryName; + + @JsonKey(defaultValue: false) + bool generateReleaseNotes = false; CreateRelease(this.tagName); - CreateRelease.from({ - @required this.tagName, - @required this.name, - @required this.targetCommitish, - @required this.isDraft, - @required this.isPrerelease, - this.body, - }); + CreateRelease.from( + {required this.tagName, + required this.name, + required this.targetCommitish, + required this.isDraft, + required this.isPrerelease, + this.body, + this.discussionCategoryName, + this.generateReleaseNotes = false}); @override bool operator ==(Object other) => @@ -195,7 +200,9 @@ class CreateRelease { name == other.name && body == other.body && isDraft == other.isDraft && - isPrerelease == other.isPrerelease; + isPrerelease == other.isPrerelease && + generateReleaseNotes == other.generateReleaseNotes && + discussionCategoryName == other.discussionCategoryName; @override int get hashCode => @@ -204,7 +211,9 @@ class CreateRelease { name.hashCode ^ body.hashCode ^ isDraft.hashCode ^ - isPrerelease.hashCode; + isPrerelease.hashCode ^ + discussionCategoryName.hashCode ^ + generateReleaseNotes.hashCode; factory CreateRelease.fromJson(Map input) => _$CreateReleaseFromJson(input); @@ -213,9 +222,9 @@ class CreateRelease { class CreateReleaseAsset { CreateReleaseAsset({ - @required this.name, - @required this.contentType, - @required this.assetData, + required this.name, + required this.contentType, + required this.assetData, this.label, }); @@ -223,7 +232,7 @@ class CreateReleaseAsset { String name; /// An alternate short description of the asset. Used in place of the filename. - String label; + String? label; /// The media type of the asset. /// @@ -237,3 +246,32 @@ class CreateReleaseAsset { /// GitHub expects the asset data in its raw binary form, rather than JSON. Uint8List assetData; } + +/// Holds release notes information +@JsonSerializable() +class ReleaseNotes { + ReleaseNotes(this.name, this.body); + String name; + String body; + + factory ReleaseNotes.fromJson(Map input) => + _$ReleaseNotesFromJson(input); + Map toJson() => _$ReleaseNotesToJson(this); +} + +@JsonSerializable() +class CreateReleaseNotes { + CreateReleaseNotes(this.owner, this.repo, this.tagName, + {this.targetCommitish, this.previousTagName, this.configurationFilePath}); + + String owner; + String repo; + String tagName; + String? targetCommitish; + String? previousTagName; + String? configurationFilePath; + + factory CreateReleaseNotes.fromJson(Map input) => + _$CreateReleaseNotesFromJson(input); + Map toJson() => _$CreateReleaseNotesToJson(this); +} diff --git a/lib/src/common/model/repos_releases.g.dart b/lib/src/common/model/repos_releases.g.dart index 19e03d22..e0596897 100644 --- a/lib/src/common/model/repos_releases.g.dart +++ b/lib/src/common/model/repos_releases.g.dart @@ -6,39 +6,36 @@ part of 'repos_releases.dart'; // JsonSerializableGenerator // ************************************************************************** -Release _$ReleaseFromJson(Map json) { - return Release( - id: json['id'] as int, - url: json['url'] as String, - htmlUrl: json['html_url'] as String, - tarballUrl: json['tarball_url'] as String, - uploadUrl: json['upload_url'] as String, - nodeId: json['node_id'] as String, - tagName: json['tag_name'] as String, - targetCommitish: json['target_commitish'] as String, - name: json['name'] as String, - body: json['body'] as String, - description: json['description'] as String, - isDraft: json['draft'] as bool, - isPrerelease: json['prerelease'] as bool, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - publishedAt: json['published_at'] == null - ? null - : DateTime.parse(json['published_at'] as String), - author: json['author'] == null - ? null - : User.fromJson(json['author'] as Map), - assets: (json['assets'] as List) - ?.map((e) => - e == null ? null : ReleaseAsset.fromJson(e as Map)) - ?.toList(), - ) - ..zipballUrl = json['zipball_url'] as String - ..assetsUrl = json['assets_url'] as String - ..errors = json['errors'] as List; -} +Release _$ReleaseFromJson(Map json) => Release( + id: (json['id'] as num?)?.toInt(), + url: json['url'] as String?, + htmlUrl: json['html_url'] as String?, + tarballUrl: json['tarball_url'] as String?, + uploadUrl: json['upload_url'] as String?, + nodeId: json['node_id'] as String?, + tagName: json['tag_name'] as String?, + targetCommitish: json['target_commitish'] as String?, + name: json['name'] as String?, + body: json['body'] as String?, + description: json['description'] as String?, + isDraft: json['draft'] as bool?, + isPrerelease: json['prerelease'] as bool?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + publishedAt: json['published_at'] == null + ? null + : DateTime.parse(json['published_at'] as String), + author: json['author'] == null + ? null + : User.fromJson(json['author'] as Map), + assets: (json['assets'] as List?) + ?.map((e) => ReleaseAsset.fromJson(e as Map)) + .toList(), + ) + ..zipballUrl = json['zipball_url'] as String? + ..assetsUrl = json['assets_url'] as String? + ..errors = json['errors'] as List?; Map _$ReleaseToJson(Release instance) => { 'url': instance.url, @@ -63,24 +60,22 @@ Map _$ReleaseToJson(Release instance) => { 'errors': instance.errors, }; -ReleaseAsset _$ReleaseAssetFromJson(Map json) { - return ReleaseAsset( - id: json['id'] as int, - name: json['name'] as String, - label: json['label'] as String, - state: json['state'] as String, - contentType: json['content_type'] as String, - size: json['size'] as int, - downloadCount: json['download_count'] as int, - browserDownloadUrl: json['browser_download_url'] as String, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - ); -} +ReleaseAsset _$ReleaseAssetFromJson(Map json) => ReleaseAsset( + id: (json['id'] as num?)?.toInt(), + name: json['name'] as String?, + label: json['label'] as String?, + state: json['state'] as String?, + contentType: json['content_type'] as String?, + size: (json['size'] as num?)?.toInt(), + downloadCount: (json['download_count'] as num?)?.toInt(), + browserDownloadUrl: json['browser_download_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + ); Map _$ReleaseAssetToJson(ReleaseAsset instance) => { @@ -96,16 +91,17 @@ Map _$ReleaseAssetToJson(ReleaseAsset instance) => 'updated_at': instance.updatedAt?.toIso8601String(), }; -CreateRelease _$CreateReleaseFromJson(Map json) { - return CreateRelease( - json['tag_name'] as String, - ) - ..targetCommitish = json['target_commitish'] as String - ..name = json['name'] as String - ..body = json['body'] as String - ..isDraft = json['draft'] as bool - ..isPrerelease = json['prerelease'] as bool; -} +CreateRelease _$CreateReleaseFromJson(Map json) => + CreateRelease( + json['tag_name'] as String?, + ) + ..targetCommitish = json['target_commitish'] as String? + ..name = json['name'] as String? + ..body = json['body'] as String? + ..isDraft = json['draft'] as bool? + ..isPrerelease = json['prerelease'] as bool? + ..discussionCategoryName = json['discussion_category_name'] as String? + ..generateReleaseNotes = json['generate_release_notes'] as bool? ?? false; Map _$CreateReleaseToJson(CreateRelease instance) => { @@ -115,4 +111,37 @@ Map _$CreateReleaseToJson(CreateRelease instance) => 'body': instance.body, 'draft': instance.isDraft, 'prerelease': instance.isPrerelease, + 'discussion_category_name': instance.discussionCategoryName, + 'generate_release_notes': instance.generateReleaseNotes, + }; + +ReleaseNotes _$ReleaseNotesFromJson(Map json) => ReleaseNotes( + json['name'] as String, + json['body'] as String, + ); + +Map _$ReleaseNotesToJson(ReleaseNotes instance) => + { + 'name': instance.name, + 'body': instance.body, + }; + +CreateReleaseNotes _$CreateReleaseNotesFromJson(Map json) => + CreateReleaseNotes( + json['owner'] as String, + json['repo'] as String, + json['tag_name'] as String, + targetCommitish: json['target_commitish'] as String?, + previousTagName: json['previous_tag_name'] as String?, + configurationFilePath: json['configuration_file_path'] as String?, + ); + +Map _$CreateReleaseNotesToJson(CreateReleaseNotes instance) => + { + 'owner': instance.owner, + 'repo': instance.repo, + 'tag_name': instance.tagName, + 'target_commitish': instance.targetCommitish, + 'previous_tag_name': instance.previousTagName, + 'configuration_file_path': instance.configurationFilePath, }; diff --git a/lib/src/common/model/repos_stats.dart b/lib/src/common/model/repos_stats.dart index 8f2a6608..449ad36c 100644 --- a/lib/src/common/model/repos_stats.dart +++ b/lib/src/common/model/repos_stats.dart @@ -5,17 +5,17 @@ import 'package:json_annotation/json_annotation.dart'; part 'repos_stats.g.dart'; /// Model class for a contributor's statistics for a repository. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class ContributorStatistics { ContributorStatistics(this.author, this.total, this.weeks); - final User author; + final User? author; /// Total Commits - final int total; + final int? total; /// Weekly Statistics - final List weeks; + final List? weeks; factory ContributorStatistics.fromJson(Map input) => _$ContributorStatisticsFromJson(input); @@ -24,26 +24,26 @@ class ContributorStatistics { /// Model class to represent the number of additions, deletions and commits /// a contributor made in a given week. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class ContributorWeekStatistics { ContributorWeekStatistics( this.start, this.additions, this.deletions, this.commits); /// Beginning of the Week (As a Unix Timestamp) @JsonKey(name: 'w') - final int start; + final int? start; /// Number of Additions @JsonKey(name: 'a') - final int additions; + final int? additions; /// Number of Deletions @JsonKey(name: 'd') - final int deletions; + final int? deletions; /// Number of Commits @JsonKey(name: 'c') - final int commits; + final int? commits; factory ContributorWeekStatistics.fromJson(Map input) => _$ContributorWeekStatisticsFromJson(input); @@ -55,7 +55,7 @@ class ContributorWeekStatistics { } /// Model class for contributor participation. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class ContributorParticipation { ContributorParticipation({ this.all, @@ -63,10 +63,10 @@ class ContributorParticipation { }); /// Commit Counts for All Users - List all; + List? all; /// Commit Counts for the Owner - List owner; + List? owner; factory ContributorParticipation.fromJson(Map input) => _$ContributorParticipationFromJson(input); @@ -74,7 +74,7 @@ class ContributorParticipation { } /// Model class for a week in a full year commit count. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class YearCommitCountWeek { YearCommitCountWeek({ this.days, @@ -83,13 +83,13 @@ class YearCommitCountWeek { }); /// Commit Counts for each day (starting with Sunday) - List days; + List? days; /// Total Commit Count - int total; + int? total; /// Timestamp for Beginning of Week - int timestamp; + int? timestamp; factory YearCommitCountWeek.fromJson(Map input) => _$YearCommitCountWeekFromJson(input); @@ -97,7 +97,7 @@ class YearCommitCountWeek { } /// Model class for a weekly change count. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class WeeklyChangesCount { WeeklyChangesCount({ this.timestamp, @@ -106,13 +106,13 @@ class WeeklyChangesCount { }); /// Timestamp for Beginning of Week - int timestamp; + int? timestamp; /// Number of Additions - int additions; + int? additions; /// Number of Deletions - int deletions; + int? deletions; factory WeeklyChangesCount.fromJson(Map input) => _$WeeklyChangesCountFromJson(input); @@ -120,7 +120,7 @@ class WeeklyChangesCount { } /// Model Class for a Punchcard Entry -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class PunchcardEntry { PunchcardEntry({ this.weekday, @@ -129,13 +129,13 @@ class PunchcardEntry { }); /// Weekday (With 0 as Sunday and 6 as Saturday) - int weekday; + int? weekday; /// Hour of Day - int hour; + int? hour; /// Number of Commits - int commits; + int? commits; factory PunchcardEntry.fromJson(Map input) => _$PunchcardEntryFromJson(input); diff --git a/lib/src/common/model/repos_stats.g.dart b/lib/src/common/model/repos_stats.g.dart index fdf25d96..83bd1650 100644 --- a/lib/src/common/model/repos_stats.g.dart +++ b/lib/src/common/model/repos_stats.g.dart @@ -7,19 +7,17 @@ part of 'repos_stats.dart'; // ************************************************************************** ContributorStatistics _$ContributorStatisticsFromJson( - Map json) { - return ContributorStatistics( - json['author'] == null - ? null - : User.fromJson(json['author'] as Map), - json['total'] as int, - (json['weeks'] as List) - ?.map((e) => e == null - ? null - : ContributorWeekStatistics.fromJson(e as Map)) - ?.toList(), - ); -} + Map json) => + ContributorStatistics( + json['author'] == null + ? null + : User.fromJson(json['author'] as Map), + (json['total'] as num?)?.toInt(), + (json['weeks'] as List?) + ?.map((e) => + ContributorWeekStatistics.fromJson(e as Map)) + .toList(), + ); Map _$ContributorStatisticsToJson( ContributorStatistics instance) => @@ -30,14 +28,13 @@ Map _$ContributorStatisticsToJson( }; ContributorWeekStatistics _$ContributorWeekStatisticsFromJson( - Map json) { - return ContributorWeekStatistics( - json['w'] as int, - json['a'] as int, - json['d'] as int, - json['c'] as int, - ); -} + Map json) => + ContributorWeekStatistics( + (json['w'] as num?)?.toInt(), + (json['a'] as num?)?.toInt(), + (json['d'] as num?)?.toInt(), + (json['c'] as num?)?.toInt(), + ); Map _$ContributorWeekStatisticsToJson( ContributorWeekStatistics instance) => @@ -49,12 +46,15 @@ Map _$ContributorWeekStatisticsToJson( }; ContributorParticipation _$ContributorParticipationFromJson( - Map json) { - return ContributorParticipation( - all: (json['all'] as List)?.map((e) => e as int)?.toList(), - owner: (json['owner'] as List)?.map((e) => e as int)?.toList(), - ); -} + Map json) => + ContributorParticipation( + all: (json['all'] as List?) + ?.map((e) => (e as num).toInt()) + .toList(), + owner: (json['owner'] as List?) + ?.map((e) => (e as num).toInt()) + .toList(), + ); Map _$ContributorParticipationToJson( ContributorParticipation instance) => @@ -63,13 +63,14 @@ Map _$ContributorParticipationToJson( 'owner': instance.owner, }; -YearCommitCountWeek _$YearCommitCountWeekFromJson(Map json) { - return YearCommitCountWeek( - days: (json['days'] as List)?.map((e) => e as int)?.toList(), - total: json['total'] as int, - timestamp: json['timestamp'] as int, - ); -} +YearCommitCountWeek _$YearCommitCountWeekFromJson(Map json) => + YearCommitCountWeek( + days: (json['days'] as List?) + ?.map((e) => (e as num).toInt()) + .toList(), + total: (json['total'] as num?)?.toInt(), + timestamp: (json['timestamp'] as num?)?.toInt(), + ); Map _$YearCommitCountWeekToJson( YearCommitCountWeek instance) => @@ -79,13 +80,12 @@ Map _$YearCommitCountWeekToJson( 'timestamp': instance.timestamp, }; -WeeklyChangesCount _$WeeklyChangesCountFromJson(Map json) { - return WeeklyChangesCount( - timestamp: json['timestamp'] as int, - additions: json['additions'] as int, - deletions: json['deletions'] as int, - ); -} +WeeklyChangesCount _$WeeklyChangesCountFromJson(Map json) => + WeeklyChangesCount( + timestamp: (json['timestamp'] as num?)?.toInt(), + additions: (json['additions'] as num?)?.toInt(), + deletions: (json['deletions'] as num?)?.toInt(), + ); Map _$WeeklyChangesCountToJson(WeeklyChangesCount instance) => { @@ -94,13 +94,12 @@ Map _$WeeklyChangesCountToJson(WeeklyChangesCount instance) => 'deletions': instance.deletions, }; -PunchcardEntry _$PunchcardEntryFromJson(Map json) { - return PunchcardEntry( - weekday: json['weekday'] as int, - hour: json['hour'] as int, - commits: json['commits'] as int, - ); -} +PunchcardEntry _$PunchcardEntryFromJson(Map json) => + PunchcardEntry( + weekday: (json['weekday'] as num?)?.toInt(), + hour: (json['hour'] as num?)?.toInt(), + commits: (json['commits'] as num?)?.toInt(), + ); Map _$PunchcardEntryToJson(PunchcardEntry instance) => { diff --git a/lib/src/common/model/repos_statuses.dart b/lib/src/common/model/repos_statuses.dart index 97b3f9a9..dd20266f 100644 --- a/lib/src/common/model/repos_statuses.dart +++ b/lib/src/common/model/repos_statuses.dart @@ -4,7 +4,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'repos_statuses.g.dart'; /// Model class for the combined status of a repository. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CombinedRepositoryStatus { CombinedRepositoryStatus({ this.state, @@ -13,11 +13,11 @@ class CombinedRepositoryStatus { this.statuses, this.repository, }); - String state; - String sha; - int totalCount; - List statuses; - Repository repository; + String? state; + String? sha; + int? totalCount; + List? statuses; + Repository? repository; factory CombinedRepositoryStatus.fromJson(Map input) => _$CombinedRepositoryStatusFromJson(input); @@ -25,7 +25,7 @@ class CombinedRepositoryStatus { } /// Model class for the status of a repository at a particular reference. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class RepositoryStatus { RepositoryStatus({ this.createdAt, @@ -35,12 +35,12 @@ class RepositoryStatus { this.description, this.context, }); - DateTime createdAt; - DateTime updatedAt; - String state; - String targetUrl; - String description; - String context; + DateTime? createdAt; + DateTime? updatedAt; + String? state; + String? targetUrl; + String? description; + String? context; factory RepositoryStatus.fromJson(Map input) => _$RepositoryStatusFromJson(input); @@ -48,15 +48,15 @@ class RepositoryStatus { } /// Model class for a new repository status to be created. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CreateStatus { CreateStatus(this.state, {this.targetUrl, this.description, this.context}); - final String state; - String description; - String context; + final String? state; + String? description; + String? context; @JsonKey(name: 'target_url') - String targetUrl; + String? targetUrl; factory CreateStatus.fromJson(Map input) => _$CreateStatusFromJson(input); diff --git a/lib/src/common/model/repos_statuses.g.dart b/lib/src/common/model/repos_statuses.g.dart index 9441832f..86b7b8e3 100644 --- a/lib/src/common/model/repos_statuses.g.dart +++ b/lib/src/common/model/repos_statuses.g.dart @@ -7,21 +7,18 @@ part of 'repos_statuses.dart'; // ************************************************************************** CombinedRepositoryStatus _$CombinedRepositoryStatusFromJson( - Map json) { - return CombinedRepositoryStatus( - state: json['state'] as String, - sha: json['sha'] as String, - totalCount: json['total_count'] as int, - statuses: (json['statuses'] as List) - ?.map((e) => e == null - ? null - : RepositoryStatus.fromJson(e as Map)) - ?.toList(), - repository: json['repository'] == null - ? null - : Repository.fromJson(json['repository'] as Map), - ); -} + Map json) => + CombinedRepositoryStatus( + state: json['state'] as String?, + sha: json['sha'] as String?, + totalCount: (json['total_count'] as num?)?.toInt(), + statuses: (json['statuses'] as List?) + ?.map((e) => RepositoryStatus.fromJson(e as Map)) + .toList(), + repository: json['repository'] == null + ? null + : Repository.fromJson(json['repository'] as Map), + ); Map _$CombinedRepositoryStatusToJson( CombinedRepositoryStatus instance) => @@ -33,20 +30,19 @@ Map _$CombinedRepositoryStatusToJson( 'repository': instance.repository, }; -RepositoryStatus _$RepositoryStatusFromJson(Map json) { - return RepositoryStatus( - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - state: json['state'] as String, - targetUrl: json['target_url'] as String, - description: json['description'] as String, - context: json['context'] as String, - ); -} +RepositoryStatus _$RepositoryStatusFromJson(Map json) => + RepositoryStatus( + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + state: json['state'] as String?, + targetUrl: json['target_url'] as String?, + description: json['description'] as String?, + context: json['context'] as String?, + ); Map _$RepositoryStatusToJson(RepositoryStatus instance) => { @@ -58,14 +54,12 @@ Map _$RepositoryStatusToJson(RepositoryStatus instance) => 'context': instance.context, }; -CreateStatus _$CreateStatusFromJson(Map json) { - return CreateStatus( - json['state'] as String, - targetUrl: json['target_url'] as String, - description: json['description'] as String, - context: json['context'] as String, - ); -} +CreateStatus _$CreateStatusFromJson(Map json) => CreateStatus( + json['state'] as String?, + targetUrl: json['target_url'] as String?, + description: json['description'] as String?, + context: json['context'] as String?, + ); Map _$CreateStatusToJson(CreateStatus instance) => { diff --git a/lib/src/common/model/search.dart b/lib/src/common/model/search.dart index 19d2cce9..c61f39e5 100644 --- a/lib/src/common/model/search.dart +++ b/lib/src/common/model/search.dart @@ -4,45 +4,46 @@ import 'package:json_annotation/json_annotation.dart'; part 'search.g.dart'; abstract class SearchResults { - int totalCount; - bool incompleteResults; - List items; + int? totalCount; + bool? incompleteResults; + List? items; } -@JsonSerializable(createToJson: false) +@JsonSerializable() class CodeSearchResults implements SearchResults { @JsonKey(name: 'total_count') @override - int totalCount; + int? totalCount; @JsonKey(name: 'incomplete_results') @override - bool incompleteResults; + bool? incompleteResults; @JsonKey(fromJson: CodeSearchItem.fromJsonList) @override - List items; + List? items; static CodeSearchResults fromJson(Map input) => _$CodeSearchResultsFromJson(input); + Map toJson() => _$CodeSearchResultsToJson(this); } -@JsonSerializable(createToJson: false) +@JsonSerializable() class CodeSearchItem { - String name; - String path; - String sha; + String? name; + String? path; + String? sha; @JsonKey(fromJson: Uri.parse) - Uri url; + Uri? url; @JsonKey(name: 'git_url', fromJson: Uri.parse) - Uri gitUrl; + Uri? gitUrl; @JsonKey(name: 'html_url', fromJson: Uri.parse) - Uri htmlUrl; + Uri? htmlUrl; - Repository repository; + Repository? repository; static CodeSearchItem fromJson(Map input) { return _$CodeSearchItemFromJson(input); @@ -57,11 +58,13 @@ class CodeSearchItem { } return result; } + + Map toJson() => _$CodeSearchItemToJson(this); } // TODO: Issue Search -// @JsonSerializable(createToJson: false) +// @JsonSerializable() // class IssueSearchResults extends SearchResults {} -// @JsonSerializable(createToJson: false) +// @JsonSerializable() // class IssueSearchItem {} diff --git a/lib/src/common/model/search.g.dart b/lib/src/common/model/search.g.dart index 03d6b850..9d606865 100644 --- a/lib/src/common/model/search.g.dart +++ b/lib/src/common/model/search.g.dart @@ -6,22 +6,38 @@ part of 'search.dart'; // JsonSerializableGenerator // ************************************************************************** -CodeSearchResults _$CodeSearchResultsFromJson(Map json) { - return CodeSearchResults() - ..totalCount = json['total_count'] as int - ..incompleteResults = json['incomplete_results'] as bool - ..items = CodeSearchItem.fromJsonList(json['items'] as List); -} +CodeSearchResults _$CodeSearchResultsFromJson(Map json) => + CodeSearchResults() + ..totalCount = (json['total_count'] as num?)?.toInt() + ..incompleteResults = json['incomplete_results'] as bool? + ..items = CodeSearchItem.fromJsonList(json['items'] as List); -CodeSearchItem _$CodeSearchItemFromJson(Map json) { - return CodeSearchItem() - ..name = json['name'] as String - ..path = json['path'] as String - ..sha = json['sha'] as String - ..url = Uri.parse(json['url'] as String) - ..gitUrl = Uri.parse(json['git_url'] as String) - ..htmlUrl = Uri.parse(json['html_url'] as String) - ..repository = json['repository'] == null - ? null - : Repository.fromJson(json['repository'] as Map); -} +Map _$CodeSearchResultsToJson(CodeSearchResults instance) => + { + 'total_count': instance.totalCount, + 'incomplete_results': instance.incompleteResults, + 'items': instance.items, + }; + +CodeSearchItem _$CodeSearchItemFromJson(Map json) => + CodeSearchItem() + ..name = json['name'] as String? + ..path = json['path'] as String? + ..sha = json['sha'] as String? + ..url = Uri.parse(json['url'] as String) + ..gitUrl = Uri.parse(json['git_url'] as String) + ..htmlUrl = Uri.parse(json['html_url'] as String) + ..repository = json['repository'] == null + ? null + : Repository.fromJson(json['repository'] as Map); + +Map _$CodeSearchItemToJson(CodeSearchItem instance) => + { + 'name': instance.name, + 'path': instance.path, + 'sha': instance.sha, + 'url': instance.url?.toString(), + 'git_url': instance.gitUrl?.toString(), + 'html_url': instance.htmlUrl?.toString(), + 'repository': instance.repository, + }; diff --git a/lib/src/common/model/timeline.dart b/lib/src/common/model/timeline.dart new file mode 100644 index 00000000..ae2c48ef --- /dev/null +++ b/lib/src/common/model/timeline.dart @@ -0,0 +1,582 @@ +import 'package:github/src/common.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'timeline.g.dart'; + +// Parts of this file were originally automatically generated from the response +// schema provided in the API documentation for GitHub's "List timeline events +// for an issue" API [1], using the `tool/process_github_schema.dart` script. +// +// Unfortunately, that schema contradicts the prose documentation [2] in a great +// variety of ways (for example none of the "common properties" are actually +// common to all the event types), so this code is an attempt to find the most +// pragmatic middleground between what is documented and what actually works. +// +// [1] https://docs.github.com/en/rest/issues/timeline?apiVersion=2022-11-28 +// [2] https://docs.github.com/en/webhooks-and-events/events/issue-event-types + +/// Model class for an issue or PR timeline event. +/// +/// This is a base class for the various event types. Events that only use the +/// default fields use this class; events that have additional fields use one +/// of the subclasses. +/// +/// The [TimelineEvent.fromJson] factory selects the right subclass based on +/// the [event] field. +/// +/// If the [event] type is not known, [TimelineEvent] is used. +/// +/// See also: https://docs.github.com/en/webhooks-and-events/events/issue-event-types +@JsonSerializable() +class TimelineEvent { + TimelineEvent({ + this.id = 0, + this.nodeId, + this.url, + this.actor, + this.event = '', + this.commitId, + this.commitUrl, + this.createdAt, + this.performedViaGithubApp, + }); + + int id; + String? nodeId; + String? url; + User? actor; + String event; + String? commitId; + String? commitUrl; + DateTime? createdAt; + GitHubApp? performedViaGithubApp; + + Map toJson() => _$TimelineEventToJson(this); + + factory TimelineEvent.fromJson(Map input) { + switch (input['event']) { + case 'added_to_project': + return ProjectEvent.fromJson(input); + case 'assigned': + return AssigneeEvent.fromJson(input); + case 'commented': + return CommentEvent.fromJson(input); + case 'committed': + return TimelineCommitEvent.fromJson(input); + case 'cross-referenced': + return CrossReferenceEvent.fromJson(input); + case 'demilestoned': + return MilestoneEvent.fromJson(input); + case 'labeled': + return LabelEvent.fromJson(input); + case 'locked': + return LockEvent.fromJson(input); + case 'milestoned': + return MilestoneEvent.fromJson(input); + case 'moved_columns_in_project': + return ProjectEvent.fromJson(input); + case 'removed_from_project': + return ProjectEvent.fromJson(input); + case 'renamed': + return RenameEvent.fromJson(input); + case 'review_dismissed': + return ReviewDismissedEvent.fromJson(input); + case 'review_requested': + return ReviewRequestEvent.fromJson(input); + case 'review_request_removed': + return ReviewRequestEvent.fromJson(input); + case 'reviewed': + return ReviewEvent.fromJson(input); + case 'unassigned': + return AssigneeEvent.fromJson(input); + case 'unlabeled': + return LabelEvent.fromJson(input); + default: + return _$TimelineEventFromJson(input); + } + } +} + +/// Labeled and Unlabeled Issue Events +@JsonSerializable() +class LabelEvent extends TimelineEvent { + LabelEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = '', // typically 'labeled' or 'unlabeled' + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.label, + }); + + IssueLabel? label; + + @override + Map toJson() => _$LabelEventToJson(this); + + factory LabelEvent.fromJson(Map input) => + _$LabelEventFromJson(input); +} + +/// Milestoned and Demilestoned Issue Event +@JsonSerializable() +class MilestoneEvent extends TimelineEvent { + MilestoneEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = '', // typically 'milestoned' or 'demilestoned' + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.milestone, + }); + + Milestone? milestone; + + @override + Map toJson() => _$MilestoneEventToJson(this); + + factory MilestoneEvent.fromJson(Map input) => + _$MilestoneEventFromJson(input); +} + +/// Renamed Issue Event +@JsonSerializable() +class RenameEvent extends TimelineEvent { + RenameEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = 'renamed', + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.rename, + }); + + Rename? rename; + + @override + Map toJson() => _$RenameEventToJson(this); + + factory RenameEvent.fromJson(Map input) => + _$RenameEventFromJson(input); +} + +/// Review Requested and Review Request Removed Issue Events +@JsonSerializable() +class ReviewRequestEvent extends TimelineEvent { + ReviewRequestEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = + '', // typically 'review_requested' or 'review_request_removed' + super.commitId, + super.commitUrl, + super.createdAt, + this.requestedReviewer, + this.requestedTeam, + this.reviewRequester, + }); + + User? requestedReviewer; + + /// Team + /// + /// Groups of organization members that gives permissions on specified repositories. + Team? requestedTeam; + + User? reviewRequester; + + @override + Map toJson() => _$ReviewRequestEventToJson(this); + + factory ReviewRequestEvent.fromJson(Map input) => + _$ReviewRequestEventFromJson(input); +} + +/// Review Dismissed Issue Event +@JsonSerializable() +class ReviewDismissedEvent extends TimelineEvent { + ReviewDismissedEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = 'review_dismissed', + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.dismissedReview, + }); + + DismissedReview? dismissedReview; + + @override + Map toJson() => _$ReviewDismissedEventToJson(this); + + factory ReviewDismissedEvent.fromJson(Map input) => + _$ReviewDismissedEventFromJson(input); +} + +/// Locked Issue Event +@JsonSerializable() +class LockEvent extends TimelineEvent { + LockEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = 'locked', + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.lockReason, + }); + + /// Example: `"off-topic"` + String? lockReason; + + @override + Map toJson() => _$LockEventToJson(this); + + factory LockEvent.fromJson(Map input) => + _$LockEventFromJson(input); +} + +/// Added to Project, +/// Moved Columns in Project, +/// Removed from Project, and +/// Converted Note to Issue +/// Issue Events. +@JsonSerializable() +class ProjectEvent extends TimelineEvent { + ProjectEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event, // typically one of 'added_to_project', 'moved_columns_in_project', 'removed_from_project', 'converted_note_to_issue' + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.projectCard, + }); + + ProjectCard? projectCard; + + @override + Map toJson() => _$ProjectEventToJson(this); + + factory ProjectEvent.fromJson(Map input) => + _$ProjectEventFromJson(input); +} + +/// Timeline Comment Event +@JsonSerializable() +class CommentEvent extends TimelineEvent { + CommentEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = 'commented', + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.authorAssociation, + this.body, + this.bodyHtml, + this.bodyText, + this.htmlUrl, + this.issueUrl, + this.reactions, + this.updatedAt, + this.user, + }); + + /// How the author is associated with the repository. + /// + /// Example: `OWNER` + String? authorAssociation; + + /// Contents of the issue comment + /// + /// Example: `What version of Safari were you using when you observed this bug?` + String? body; + + String? bodyHtml; + String? bodyText; + + String? htmlUrl; + + String? issueUrl; + + ReactionRollup? reactions; + + DateTime? updatedAt; + + User? user; + + @override + Map toJson() => _$CommentEventToJson(this); + + factory CommentEvent.fromJson(Map input) => + _$CommentEventFromJson(input); +} + +/// Timeline Cross Referenced Event +@JsonSerializable() +class CrossReferenceEvent extends TimelineEvent { + CrossReferenceEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = 'cross-referenced', + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.source, + this.updatedAt, + }); + + Source? source; + + DateTime? updatedAt; + + @override + Map toJson() => _$CrossReferenceEventToJson(this); + + factory CrossReferenceEvent.fromJson(Map input) => + _$CrossReferenceEventFromJson(input); +} + +/// Timeline Committed Event +@JsonSerializable() +class TimelineCommitEvent extends TimelineEvent { + TimelineCommitEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = 'committed', + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.author, + this.committer, + this.htmlUrl, + this.message, + this.parents, + this.sha, + this.tree, + this.verification, + }); + + User? author; + + /// Identifying information for the git-user + User? committer; + + /// Format: uri + String? htmlUrl; + + /// Message describing the purpose of the commit + String? message; + + List? parents; + + /// SHA for the commit + /// + /// Example: `7638417db6d59f3c431d3e1f261cc637155684cd` + String? sha; + + Tree? tree; + + Verification? verification; + + @override + Map toJson() => _$TimelineCommitEventToJson(this); + + factory TimelineCommitEvent.fromJson(Map input) => + _$TimelineCommitEventFromJson(input); +} + +/// Timeline Reviewed Event +@JsonSerializable() +class ReviewEvent extends TimelineEvent { + ReviewEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = 'reviewed', + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.authorAssociation, + this.body, + this.bodyHtml, + this.bodyText, + this.htmlUrl, + this.links, + this.pullRequestUrl, + this.state, + this.submittedAt, + this.user, + }); + + /// How the author is associated with the repository. + /// + /// Example: `OWNER` + String? authorAssociation; + + /// The text of the review. + /// + /// Example: `This looks great.` + String? body; + + String? bodyHtml; + String? bodyText; + + /// Example: `https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80` + String? htmlUrl; + + @JsonKey(name: '_links') + ReviewLinks? links; + + /// Example: `https://api.github.com/repos/octocat/Hello-World/pulls/12` + String? pullRequestUrl; + + /// Example: `CHANGES_REQUESTED` + String? state; + + DateTime? submittedAt; + + User? user; + + @override + Map toJson() => _$ReviewEventToJson(this); + + factory ReviewEvent.fromJson(Map input) => + _$ReviewEventFromJson(input); +} + +/// Timeline Line Commented Event +@JsonSerializable() +class TimelineLineCommentedEvent extends TimelineEvent { + TimelineLineCommentedEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = '', + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.comments, + }); + + List? comments; + + @override + Map toJson() => _$TimelineLineCommentedEventToJson(this); + + factory TimelineLineCommentedEvent.fromJson(Map input) => + _$TimelineLineCommentedEventFromJson(input); +} + +/// Timeline Commit Commented Event +@JsonSerializable() +class TimelineCommitCommentedEvent extends TimelineEvent { + TimelineCommitCommentedEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = '', + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.comments, + }); + + List? comments; + + @override + Map toJson() => _$TimelineCommitCommentedEventToJson(this); + + factory TimelineCommitCommentedEvent.fromJson(Map input) => + _$TimelineCommitCommentedEventFromJson(input); +} + +/// Timeline Assigned and Timeline Unassigned Issue Events +@JsonSerializable() +class AssigneeEvent extends TimelineEvent { + AssigneeEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event, // typically 'assigned' or 'unassigned' + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.assignee, + }); + + User? assignee; + + @override + Map toJson() => _$AssigneeEventToJson(this); + + factory AssigneeEvent.fromJson(Map input) => + _$AssigneeEventFromJson(input); +} + +/// State Change Issue Event +@JsonSerializable() +class StateChangeIssueEvent extends TimelineEvent { + StateChangeIssueEvent({ + super.id = 0, + super.nodeId, + super.url, + super.actor, + super.event = '', + super.commitId, + super.commitUrl, + super.createdAt, + super.performedViaGithubApp, + this.stateReason, + }); + + String? stateReason; + + @override + Map toJson() => _$StateChangeIssueEventToJson(this); + + factory StateChangeIssueEvent.fromJson(Map input) => + _$StateChangeIssueEventFromJson(input); +} diff --git a/lib/src/common/model/timeline.g.dart b/lib/src/common/model/timeline.g.dart new file mode 100644 index 00000000..be7d916d --- /dev/null +++ b/lib/src/common/model/timeline.g.dart @@ -0,0 +1,671 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'timeline.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +TimelineEvent _$TimelineEventFromJson(Map json) => + TimelineEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? '', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + ); + +Map _$TimelineEventToJson(TimelineEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + }; + +LabelEvent _$LabelEventFromJson(Map json) => LabelEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? '', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + label: json['label'] == null + ? null + : IssueLabel.fromJson(json['label'] as Map), + ); + +Map _$LabelEventToJson(LabelEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'label': instance.label, + }; + +MilestoneEvent _$MilestoneEventFromJson(Map json) => + MilestoneEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? '', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + milestone: json['milestone'] == null + ? null + : Milestone.fromJson(json['milestone'] as Map), + ); + +Map _$MilestoneEventToJson(MilestoneEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'milestone': instance.milestone, + }; + +RenameEvent _$RenameEventFromJson(Map json) => RenameEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? 'renamed', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + rename: json['rename'] == null + ? null + : Rename.fromJson(json['rename'] as Map), + ); + +Map _$RenameEventToJson(RenameEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'rename': instance.rename, + }; + +ReviewRequestEvent _$ReviewRequestEventFromJson(Map json) => + ReviewRequestEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? '', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + requestedReviewer: json['requested_reviewer'] == null + ? null + : User.fromJson(json['requested_reviewer'] as Map), + requestedTeam: json['requested_team'] == null + ? null + : Team.fromJson(json['requested_team'] as Map), + reviewRequester: json['review_requester'] == null + ? null + : User.fromJson(json['review_requester'] as Map), + )..performedViaGithubApp = json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map); + +Map _$ReviewRequestEventToJson(ReviewRequestEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'requested_reviewer': instance.requestedReviewer, + 'requested_team': instance.requestedTeam, + 'review_requester': instance.reviewRequester, + }; + +ReviewDismissedEvent _$ReviewDismissedEventFromJson( + Map json) => + ReviewDismissedEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? 'review_dismissed', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + dismissedReview: json['dismissed_review'] == null + ? null + : DismissedReview.fromJson( + json['dismissed_review'] as Map), + ); + +Map _$ReviewDismissedEventToJson( + ReviewDismissedEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'dismissed_review': instance.dismissedReview, + }; + +LockEvent _$LockEventFromJson(Map json) => LockEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? 'locked', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + lockReason: json['lock_reason'] as String?, + ); + +Map _$LockEventToJson(LockEvent instance) => { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'lock_reason': instance.lockReason, + }; + +ProjectEvent _$ProjectEventFromJson(Map json) => ProjectEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? '', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + projectCard: json['project_card'] == null + ? null + : ProjectCard.fromJson(json['project_card'] as Map), + ); + +Map _$ProjectEventToJson(ProjectEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'project_card': instance.projectCard, + }; + +CommentEvent _$CommentEventFromJson(Map json) => CommentEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? 'commented', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + authorAssociation: json['author_association'] as String?, + body: json['body'] as String?, + bodyHtml: json['body_html'] as String?, + bodyText: json['body_text'] as String?, + htmlUrl: json['html_url'] as String?, + issueUrl: json['issue_url'] as String?, + reactions: json['reactions'] == null + ? null + : ReactionRollup.fromJson(json['reactions'] as Map), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + ); + +Map _$CommentEventToJson(CommentEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'author_association': instance.authorAssociation, + 'body': instance.body, + 'body_html': instance.bodyHtml, + 'body_text': instance.bodyText, + 'html_url': instance.htmlUrl, + 'issue_url': instance.issueUrl, + 'reactions': instance.reactions, + 'updated_at': instance.updatedAt?.toIso8601String(), + 'user': instance.user, + }; + +CrossReferenceEvent _$CrossReferenceEventFromJson(Map json) => + CrossReferenceEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? 'cross-referenced', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + source: json['source'] == null + ? null + : Source.fromJson(json['source'] as Map), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + ); + +Map _$CrossReferenceEventToJson( + CrossReferenceEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'source': instance.source, + 'updated_at': instance.updatedAt?.toIso8601String(), + }; + +TimelineCommitEvent _$TimelineCommitEventFromJson(Map json) => + TimelineCommitEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? 'committed', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + author: json['author'] == null + ? null + : User.fromJson(json['author'] as Map), + committer: json['committer'] == null + ? null + : User.fromJson(json['committer'] as Map), + htmlUrl: json['html_url'] as String?, + message: json['message'] as String?, + parents: (json['parents'] as List?) + ?.map((e) => Tree.fromJson(e as Map)) + .toList(), + sha: json['sha'] as String?, + tree: json['tree'] == null + ? null + : Tree.fromJson(json['tree'] as Map), + verification: json['verification'] == null + ? null + : Verification.fromJson(json['verification'] as Map), + ); + +Map _$TimelineCommitEventToJson( + TimelineCommitEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'author': instance.author, + 'committer': instance.committer, + 'html_url': instance.htmlUrl, + 'message': instance.message, + 'parents': instance.parents, + 'sha': instance.sha, + 'tree': instance.tree, + 'verification': instance.verification, + }; + +ReviewEvent _$ReviewEventFromJson(Map json) => ReviewEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? 'reviewed', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + authorAssociation: json['author_association'] as String?, + body: json['body'] as String?, + bodyHtml: json['body_html'] as String?, + bodyText: json['body_text'] as String?, + htmlUrl: json['html_url'] as String?, + links: json['_links'] == null + ? null + : ReviewLinks.fromJson(json['_links'] as Map), + pullRequestUrl: json['pull_request_url'] as String?, + state: json['state'] as String?, + submittedAt: json['submitted_at'] == null + ? null + : DateTime.parse(json['submitted_at'] as String), + user: json['user'] == null + ? null + : User.fromJson(json['user'] as Map), + ); + +Map _$ReviewEventToJson(ReviewEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'author_association': instance.authorAssociation, + 'body': instance.body, + 'body_html': instance.bodyHtml, + 'body_text': instance.bodyText, + 'html_url': instance.htmlUrl, + '_links': instance.links, + 'pull_request_url': instance.pullRequestUrl, + 'state': instance.state, + 'submitted_at': instance.submittedAt?.toIso8601String(), + 'user': instance.user, + }; + +TimelineLineCommentedEvent _$TimelineLineCommentedEventFromJson( + Map json) => + TimelineLineCommentedEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? '', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + comments: (json['comments'] as List?) + ?.map((e) => + PullRequestReviewComment.fromJson(e as Map)) + .toList(), + ); + +Map _$TimelineLineCommentedEventToJson( + TimelineLineCommentedEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'comments': instance.comments, + }; + +TimelineCommitCommentedEvent _$TimelineCommitCommentedEventFromJson( + Map json) => + TimelineCommitCommentedEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? '', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + comments: (json['comments'] as List?) + ?.map((e) => CommitComment.fromJson(e as Map)) + .toList(), + ); + +Map _$TimelineCommitCommentedEventToJson( + TimelineCommitCommentedEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'comments': instance.comments, + }; + +AssigneeEvent _$AssigneeEventFromJson(Map json) => + AssigneeEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? '', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + assignee: json['assignee'] == null + ? null + : User.fromJson(json['assignee'] as Map), + ); + +Map _$AssigneeEventToJson(AssigneeEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'assignee': instance.assignee, + }; + +StateChangeIssueEvent _$StateChangeIssueEventFromJson( + Map json) => + StateChangeIssueEvent( + id: (json['id'] as num?)?.toInt() ?? 0, + nodeId: json['node_id'] as String?, + url: json['url'] as String?, + actor: json['actor'] == null + ? null + : User.fromJson(json['actor'] as Map), + event: json['event'] as String? ?? '', + commitId: json['commit_id'] as String?, + commitUrl: json['commit_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + performedViaGithubApp: json['performed_via_github_app'] == null + ? null + : GitHubApp.fromJson( + json['performed_via_github_app'] as Map), + stateReason: json['state_reason'] as String?, + ); + +Map _$StateChangeIssueEventToJson( + StateChangeIssueEvent instance) => + { + 'id': instance.id, + 'node_id': instance.nodeId, + 'url': instance.url, + 'actor': instance.actor, + 'event': instance.event, + 'commit_id': instance.commitId, + 'commit_url': instance.commitUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'performed_via_github_app': instance.performedViaGithubApp, + 'state_reason': instance.stateReason, + }; diff --git a/lib/src/common/model/timeline_support.dart b/lib/src/common/model/timeline_support.dart new file mode 100644 index 00000000..f2b03c62 --- /dev/null +++ b/lib/src/common/model/timeline_support.dart @@ -0,0 +1,562 @@ +import 'package:github/src/common.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'timeline_support.g.dart'; + +/// GitHub app +/// +/// GitHub apps are a new way to extend GitHub. They can be installed directly +/// on organizations and user accounts and granted access to specific repositories. +/// They come with granular permissions and built-in webhooks. GitHub apps are +/// first class actors within GitHub. +@JsonSerializable() +class GitHubApp { + GitHubApp({ + this.clientId, + this.clientSecret, + this.createdAt, + this.description, + this.events, + this.externalUrl, + this.htmlUrl, + this.id, + this.installationsCount, + this.name, + this.nodeId, + this.owner, + this.pem, + this.permissions, + this.slug, + this.updatedAt, + this.webhookSecret, + }); + + /// Example: `"Iv1.25b5d1e65ffc4022"` + final String? clientId; + + /// Example: `"1d4b2097ac622ba702d19de498f005747a8b21d3"` + final String? clientSecret; + + final DateTime? createdAt; + + final String? description; + + /// The list of events for the GitHub app + /// + /// Example: `label` + /// + /// Example: `deployment` + final List? events; + + /// Example: `https://example.com` + final String? externalUrl; + + /// Example: `https://github.com/apps/super-ci` + final String? htmlUrl; + + /// Unique identifier of the GitHub app + final int? id; + + /// The number of installations associated with the GitHub app + final int? installationsCount; + + /// The name of the GitHub app + /// + /// Example: `Probot Owners` + final String? name; + + /// Example: `MDExOkludGVncmF0aW9uMQ==` + final String? nodeId; + + final User? owner; + + /// Example: + /// + /// ``` + /// -----BEGIN RSA PRIVATE KEY----- + /// MIIEogIBAAKCAQEArYxrNYD/iT5CZVpRJu4rBKmmze3PVmT/gCo2ATUvDvZTPTey + /// xcGJ3vvrJXazKk06pN05TN29o98jrYz4cengG3YGsXPNEpKsIrEl8NhbnxapEnM9 + /// JCMRe0P5JcPsfZlX6hmiT7136GRWiGOUba2X9+HKh8QJVLG5rM007TBER9/z9mWm + /// rJuNh+m5l320oBQY/Qq3A7wzdEfZw8qm/mIN0FCeoXH1L6B8xXWaAYBwhTEh6SSn + /// ZHlO1Xu1JWDmAvBCi0RO5aRSKM8q9QEkvvHP4yweAtK3N8+aAbZ7ovaDhyGz8r6r + /// zhU1b8Uo0Z2ysf503WqzQgIajr7Fry7/kUwpgQIDAQABAoIBADwJp80Ko1xHPZDy + /// fcCKBDfIuPvkmSW6KumbsLMaQv1aGdHDwwTGv3t0ixSay8CGlxMRtRDyZPib6SvQ + /// 6OH/lpfpbMdW2ErkksgtoIKBVrDilfrcAvrNZu7NxRNbhCSvN8q0s4ICecjbbVQh + /// nueSdlA6vGXbW58BHMq68uRbHkP+k+mM9U0mDJ1HMch67wlg5GbayVRt63H7R2+r + /// Vxcna7B80J/lCEjIYZznawgiTvp3MSanTglqAYi+m1EcSsP14bJIB9vgaxS79kTu + /// oiSo93leJbBvuGo8QEiUqTwMw4tDksmkLsoqNKQ1q9P7LZ9DGcujtPy4EZsamSJT + /// y8OJt0ECgYEA2lxOxJsQk2kI325JgKFjo92mQeUObIvPfSNWUIZQDTjniOI6Gv63 + /// GLWVFrZcvQBWjMEQraJA9xjPbblV8PtfO87MiJGLWCHFxmPz2dzoedN+2Coxom8m + /// V95CLz8QUShuao6u/RYcvUaZEoYs5bHcTmy5sBK80JyEmafJPtCQVxMCgYEAy3ar + /// Zr3yv4xRPEPMat4rseswmuMooSaK3SKub19WFI5IAtB/e7qR1Rj9JhOGcZz+OQrl + /// T78O2OFYlgOIkJPvRMrPpK5V9lslc7tz1FSh3BZMRGq5jSyD7ETSOQ0c8T2O/s7v + /// beEPbVbDe4mwvM24XByH0GnWveVxaDl51ABD65sCgYB3ZAspUkOA5egVCh8kNpnd + /// Sd6SnuQBE3ySRlT2WEnCwP9Ph6oPgn+oAfiPX4xbRqkL8q/k0BdHQ4h+zNwhk7+h + /// WtPYRAP1Xxnc/F+jGjb+DVaIaKGU18MWPg7f+FI6nampl3Q0KvfxwX0GdNhtio8T + /// Tj1E+SnFwh56SRQuxSh2gwKBgHKjlIO5NtNSflsUYFM+hyQiPiqnHzddfhSG+/3o + /// m5nNaSmczJesUYreH5San7/YEy2UxAugvP7aSY2MxB+iGsiJ9WD2kZzTUlDZJ7RV + /// UzWsoqBR+eZfVJ2FUWWvy8TpSG6trh4dFxImNtKejCR1TREpSiTV3Zb1dmahK9GV + /// rK9NAoGAbBxRLoC01xfxCTgt5BDiBcFVh4fp5yYKwavJPLzHSpuDOrrI9jDn1oKN + /// onq5sDU1i391zfQvdrbX4Ova48BN+B7p63FocP/MK5tyyBoT8zQEk2+vWDOw7H/Z + /// u5dTCPxTIsoIwUw1I+7yIxqJzLPFgR2gVBwY1ra/8iAqCj+zeBw= + /// -----END RSA PRIVATE KEY----- + /// ``` + final String? pem; + + /// The set of permissions for the GitHub app + final Permissions? permissions; + + /// The slug name of the GitHub app + /// + /// Example: `probot-owners` + final String? slug; + + final DateTime? updatedAt; + + /// Example: `"6fba8f2fc8a7e8f2cca5577eddd82ca7586b3b6b"` + final String? webhookSecret; + + Map toJson() => _$GitHubAppToJson(this); + + factory GitHubApp.fromJson(Map input) => + _$GitHubAppFromJson(input); +} + +@JsonSerializable() +class Rename { + Rename({ + this.from, + this.to, + }); + + final String? from; + final String? to; + + Map toJson() => _$RenameToJson(this); + + factory Rename.fromJson(Map input) => + _$RenameFromJson(input); +} + +@JsonSerializable() +class DismissedReview { + DismissedReview({ + this.dismissalCommitId, + this.dismissalMessage, + this.reviewId, + this.state, + }); + + final String? dismissalCommitId; + final String? dismissalMessage; + final int? reviewId; + final String? state; + + Map toJson() => _$DismissedReviewToJson(this); + + factory DismissedReview.fromJson(Map input) => + _$DismissedReviewFromJson(input); +} + +@JsonSerializable() +class ProjectCard { + ProjectCard({ + this.columnName, + this.id, + this.previousColumnName, + this.projectId, + this.projectUrl, + this.url, + }); + + final String? columnName; + final int? id; + final String? previousColumnName; + final int? projectId; + final String? projectUrl; + final String? url; + + Map toJson() => _$ProjectCardToJson(this); + + factory ProjectCard.fromJson(Map input) => + _$ProjectCardFromJson(input); +} + +@JsonSerializable() +class Source { + Source({ + this.issue, + this.type, + }); + + final Issue? issue; + final String? type; + + Map toJson() => _$SourceToJson(this); + + factory Source.fromJson(Map input) => + _$SourceFromJson(input); +} + +/// License +@JsonSerializable() +class License { + License({ + this.htmlUrl, + this.key, + this.name, + this.nodeId, + this.spdxId, + this.url, + }); + + final String? htmlUrl; + + /// Example: `mit` + final String? key; + + /// Example: `MIT License` + final String? name; + + /// Example: `MDc6TGljZW5zZW1pdA==` + final String? nodeId; + + /// Example: `MIT` + final String? spdxId; + + /// Example: `https://api.github.com/licenses/mit` + final String? url; + + Map toJson() => _$LicenseToJson(this); + + factory License.fromJson(Map input) => + _$LicenseFromJson(input); +} + +@JsonSerializable() +class TemplateRepository { + TemplateRepository({ + this.allowAutoMerge, + this.allowMergeCommit, + this.allowRebaseMerge, + this.allowSquashMerge, + this.allowUpdateBranch, + this.archiveUrl, + this.archived, + this.assigneesUrl, + this.blobsUrl, + this.branchesUrl, + this.cloneUrl, + this.collaboratorsUrl, + this.commentsUrl, + this.commitsUrl, + this.compareUrl, + this.contentsUrl, + this.contributorsUrl, + this.createdAt, + this.defaultBranch, + this.deleteBranchOnMerge, + this.deploymentsUrl, + this.description, + this.disabled, + this.downloadsUrl, + this.eventsUrl, + this.fork, + this.forksCount, + this.forksUrl, + this.fullName, + this.gitCommitsUrl, + this.gitRefsUrl, + this.gitTagsUrl, + this.gitUrl, + this.hasDownloads, + this.hasIssues, + this.hasPages, + this.hasProjects, + this.hasWiki, + this.homepage, + this.hooksUrl, + this.htmlUrl, + this.id, + this.isTemplate, + this.issueCommentUrl, + this.issueEventsUrl, + this.issuesUrl, + this.keysUrl, + this.labelsUrl, + this.language, + this.languagesUrl, + this.mergeCommitMessage, + this.mergeCommitTitle, + this.mergesUrl, + this.milestonesUrl, + this.mirrorUrl, + this.name, + this.networkCount, + this.nodeId, + this.notificationsUrl, + this.openIssuesCount, + this.owner, + this.permissions, + this.private, + this.pullsUrl, + this.pushedAt, + this.releasesUrl, + this.size, + this.squashMergeCommitMessage, + this.squashMergeCommitTitle, + this.sshUrl, + this.stargazersCount, + this.stargazersUrl, + this.statusesUrl, + this.subscribersCount, + this.subscribersUrl, + this.subscriptionUrl, + this.svnUrl, + this.tagsUrl, + this.teamsUrl, + this.tempCloneToken, + this.topics, + this.treesUrl, + this.updatedAt, + this.url, + this.visibility, + this.watchersCount, + }); + + final bool? allowAutoMerge; + final bool? allowMergeCommit; + final bool? allowRebaseMerge; + final bool? allowSquashMerge; + final bool? allowUpdateBranch; + final String? archiveUrl; + final bool? archived; + final String? assigneesUrl; + final String? blobsUrl; + final String? branchesUrl; + final String? cloneUrl; + final String? collaboratorsUrl; + final String? commentsUrl; + final String? commitsUrl; + final String? compareUrl; + final String? contentsUrl; + final String? contributorsUrl; + final DateTime? createdAt; + final String? defaultBranch; + final bool? deleteBranchOnMerge; + final String? deploymentsUrl; + final String? description; + final bool? disabled; + final String? downloadsUrl; + final String? eventsUrl; + final bool? fork; + final int? forksCount; + final String? forksUrl; + final String? fullName; + final String? gitCommitsUrl; + final String? gitRefsUrl; + final String? gitTagsUrl; + final String? gitUrl; + final bool? hasDownloads; + final bool? hasIssues; + final bool? hasPages; + final bool? hasProjects; + final bool? hasWiki; + final String? homepage; + final String? hooksUrl; + final String? htmlUrl; + final int? id; + final bool? isTemplate; + final String? issueCommentUrl; + final String? issueEventsUrl; + final String? issuesUrl; + final String? keysUrl; + final String? labelsUrl; + final String? language; + final String? languagesUrl; + + /// The default value for a merge commit message. + /// + /// - `PR_TITLE` - default to the pull request's title. + /// - `PR_BODY` - default to the pull request's body. + /// - `BLANK` - default to a blank commit message. + final String? mergeCommitMessage; + + /// The default value for a merge commit title. + /// + /// - `PR_TITLE` - default to the pull request's title. + /// - `MERGE_MESSAGE` - default to the classic title for a merge message (e.g., + /// Merge pull request #123 from branch-name). + final String? mergeCommitTitle; + + final String? mergesUrl; + final String? milestonesUrl; + final String? mirrorUrl; + final String? name; + final int? networkCount; + final String? nodeId; + final String? notificationsUrl; + final int? openIssuesCount; + final Owner? owner; + final Permissions? permissions; + final bool? private; + final String? pullsUrl; + final DateTime? pushedAt; + final String? releasesUrl; + final int? size; + + /// The default value for a squash merge commit message: + /// + /// - `PR_BODY` - default to the pull request's body. + /// - `COMMIT_MESSAGES` - default to the branch's commit messages. + /// - `BLANK` - default to a blank commit message. + final String? squashMergeCommitMessage; + + /// The default value for a squash merge commit title: + /// + /// - `PR_TITLE` - default to the pull request's title. + /// - `COMMIT_OR_PR_TITLE` - default to the commit's title (if only one commit) + /// or the pull request's title (when more than one commit). + final String? squashMergeCommitTitle; + + final String? sshUrl; + final int? stargazersCount; + final String? stargazersUrl; + final String? statusesUrl; + final int? subscribersCount; + final String? subscribersUrl; + final String? subscriptionUrl; + final String? svnUrl; + final String? tagsUrl; + final String? teamsUrl; + final String? tempCloneToken; + final List? topics; + final String? treesUrl; + final DateTime? updatedAt; + final String? url; + final String? visibility; + final int? watchersCount; + + Map toJson() => _$TemplateRepositoryToJson(this); + + factory TemplateRepository.fromJson(Map input) => + _$TemplateRepositoryFromJson(input); +} + +@JsonSerializable() +class Owner { + Owner({ + this.avatarUrl, + this.eventsUrl, + this.followersUrl, + this.followingUrl, + this.gistsUrl, + this.gravatarId, + this.htmlUrl, + this.id, + this.login, + this.nodeId, + this.organizationsUrl, + this.receivedEventsUrl, + this.reposUrl, + this.siteAdmin, + this.starredUrl, + this.subscriptionsUrl, + this.type, + this.url, + }); + + final String? avatarUrl; + final String? eventsUrl; + final String? followersUrl; + final String? followingUrl; + final String? gistsUrl; + final String? gravatarId; + final String? htmlUrl; + final int? id; + final String? login; + final String? nodeId; + final String? organizationsUrl; + final String? receivedEventsUrl; + final String? reposUrl; + final bool? siteAdmin; + final String? starredUrl; + final String? subscriptionsUrl; + final String? type; + final String? url; + + Map toJson() => _$OwnerToJson(this); + + factory Owner.fromJson(Map input) => _$OwnerFromJson(input); +} + +@JsonSerializable() +class Tree { + Tree({ + this.sha, + this.url, + this.htmlUrl, + }); + + /// SHA for the commit + /// + /// Example: `7638417db6d59f3c431d3e1f261cc637155684cd` + final String? sha; + + final String? url; + + final String? htmlUrl; + + Map toJson() => _$TreeToJson(this); + + factory Tree.fromJson(Map input) => _$TreeFromJson(input); +} + +@JsonSerializable() +class Verification { + Verification({ + this.payload, + this.reason, + this.signature, + this.verified, + }); + + final String? payload; + final String? reason; + final String? signature; + final bool? verified; + + Map toJson() => _$VerificationToJson(this); + + factory Verification.fromJson(Map input) => + _$VerificationFromJson(input); +} + +@JsonSerializable() +class HtmlLink { + HtmlLink({ + this.href, + }); + + final String? href; + + Map toJson() => _$HtmlLinkToJson(this); + + factory HtmlLink.fromJson(Map input) => + _$HtmlLinkFromJson(input); +} + +@JsonSerializable() +class PullRequestLink { + PullRequestLink({ + this.href, + }); + + /// Example: `https://api.github.com/repos/octocat/Hello-World/pulls/1` + final String? href; + + Map toJson() => _$PullRequestLinkToJson(this); + + factory PullRequestLink.fromJson(Map input) => + _$PullRequestLinkFromJson(input); +} diff --git a/lib/src/common/model/timeline_support.g.dart b/lib/src/common/model/timeline_support.g.dart new file mode 100644 index 00000000..83adf60a --- /dev/null +++ b/lib/src/common/model/timeline_support.g.dart @@ -0,0 +1,409 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'timeline_support.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GitHubApp _$GitHubAppFromJson(Map json) => GitHubApp( + clientId: json['client_id'] as String?, + clientSecret: json['client_secret'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + description: json['description'] as String?, + events: + (json['events'] as List?)?.map((e) => e as String).toList(), + externalUrl: json['external_url'] as String?, + htmlUrl: json['html_url'] as String?, + id: (json['id'] as num?)?.toInt(), + installationsCount: (json['installations_count'] as num?)?.toInt(), + name: json['name'] as String?, + nodeId: json['node_id'] as String?, + owner: json['owner'] == null + ? null + : User.fromJson(json['owner'] as Map), + pem: json['pem'] as String?, + permissions: json['permissions'] == null + ? null + : Permissions.fromJson(json['permissions'] as Map), + slug: json['slug'] as String?, + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + webhookSecret: json['webhook_secret'] as String?, + ); + +Map _$GitHubAppToJson(GitHubApp instance) => { + 'client_id': instance.clientId, + 'client_secret': instance.clientSecret, + 'created_at': instance.createdAt?.toIso8601String(), + 'description': instance.description, + 'events': instance.events, + 'external_url': instance.externalUrl, + 'html_url': instance.htmlUrl, + 'id': instance.id, + 'installations_count': instance.installationsCount, + 'name': instance.name, + 'node_id': instance.nodeId, + 'owner': instance.owner, + 'pem': instance.pem, + 'permissions': instance.permissions, + 'slug': instance.slug, + 'updated_at': instance.updatedAt?.toIso8601String(), + 'webhook_secret': instance.webhookSecret, + }; + +Rename _$RenameFromJson(Map json) => Rename( + from: json['from'] as String?, + to: json['to'] as String?, + ); + +Map _$RenameToJson(Rename instance) => { + 'from': instance.from, + 'to': instance.to, + }; + +DismissedReview _$DismissedReviewFromJson(Map json) => + DismissedReview( + dismissalCommitId: json['dismissal_commit_id'] as String?, + dismissalMessage: json['dismissal_message'] as String?, + reviewId: (json['review_id'] as num?)?.toInt(), + state: json['state'] as String?, + ); + +Map _$DismissedReviewToJson(DismissedReview instance) => + { + 'dismissal_commit_id': instance.dismissalCommitId, + 'dismissal_message': instance.dismissalMessage, + 'review_id': instance.reviewId, + 'state': instance.state, + }; + +ProjectCard _$ProjectCardFromJson(Map json) => ProjectCard( + columnName: json['column_name'] as String?, + id: (json['id'] as num?)?.toInt(), + previousColumnName: json['previous_column_name'] as String?, + projectId: (json['project_id'] as num?)?.toInt(), + projectUrl: json['project_url'] as String?, + url: json['url'] as String?, + ); + +Map _$ProjectCardToJson(ProjectCard instance) => + { + 'column_name': instance.columnName, + 'id': instance.id, + 'previous_column_name': instance.previousColumnName, + 'project_id': instance.projectId, + 'project_url': instance.projectUrl, + 'url': instance.url, + }; + +Source _$SourceFromJson(Map json) => Source( + issue: json['issue'] == null + ? null + : Issue.fromJson(json['issue'] as Map), + type: json['type'] as String?, + ); + +Map _$SourceToJson(Source instance) => { + 'issue': instance.issue, + 'type': instance.type, + }; + +License _$LicenseFromJson(Map json) => License( + htmlUrl: json['html_url'] as String?, + key: json['key'] as String?, + name: json['name'] as String?, + nodeId: json['node_id'] as String?, + spdxId: json['spdx_id'] as String?, + url: json['url'] as String?, + ); + +Map _$LicenseToJson(License instance) => { + 'html_url': instance.htmlUrl, + 'key': instance.key, + 'name': instance.name, + 'node_id': instance.nodeId, + 'spdx_id': instance.spdxId, + 'url': instance.url, + }; + +TemplateRepository _$TemplateRepositoryFromJson(Map json) => + TemplateRepository( + allowAutoMerge: json['allow_auto_merge'] as bool?, + allowMergeCommit: json['allow_merge_commit'] as bool?, + allowRebaseMerge: json['allow_rebase_merge'] as bool?, + allowSquashMerge: json['allow_squash_merge'] as bool?, + allowUpdateBranch: json['allow_update_branch'] as bool?, + archiveUrl: json['archive_url'] as String?, + archived: json['archived'] as bool?, + assigneesUrl: json['assignees_url'] as String?, + blobsUrl: json['blobs_url'] as String?, + branchesUrl: json['branches_url'] as String?, + cloneUrl: json['clone_url'] as String?, + collaboratorsUrl: json['collaborators_url'] as String?, + commentsUrl: json['comments_url'] as String?, + commitsUrl: json['commits_url'] as String?, + compareUrl: json['compare_url'] as String?, + contentsUrl: json['contents_url'] as String?, + contributorsUrl: json['contributors_url'] as String?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + defaultBranch: json['default_branch'] as String?, + deleteBranchOnMerge: json['delete_branch_on_merge'] as bool?, + deploymentsUrl: json['deployments_url'] as String?, + description: json['description'] as String?, + disabled: json['disabled'] as bool?, + downloadsUrl: json['downloads_url'] as String?, + eventsUrl: json['events_url'] as String?, + fork: json['fork'] as bool?, + forksCount: (json['forks_count'] as num?)?.toInt(), + forksUrl: json['forks_url'] as String?, + fullName: json['full_name'] as String?, + gitCommitsUrl: json['git_commits_url'] as String?, + gitRefsUrl: json['git_refs_url'] as String?, + gitTagsUrl: json['git_tags_url'] as String?, + gitUrl: json['git_url'] as String?, + hasDownloads: json['has_downloads'] as bool?, + hasIssues: json['has_issues'] as bool?, + hasPages: json['has_pages'] as bool?, + hasProjects: json['has_projects'] as bool?, + hasWiki: json['has_wiki'] as bool?, + homepage: json['homepage'] as String?, + hooksUrl: json['hooks_url'] as String?, + htmlUrl: json['html_url'] as String?, + id: (json['id'] as num?)?.toInt(), + isTemplate: json['is_template'] as bool?, + issueCommentUrl: json['issue_comment_url'] as String?, + issueEventsUrl: json['issue_events_url'] as String?, + issuesUrl: json['issues_url'] as String?, + keysUrl: json['keys_url'] as String?, + labelsUrl: json['labels_url'] as String?, + language: json['language'] as String?, + languagesUrl: json['languages_url'] as String?, + mergeCommitMessage: json['merge_commit_message'] as String?, + mergeCommitTitle: json['merge_commit_title'] as String?, + mergesUrl: json['merges_url'] as String?, + milestonesUrl: json['milestones_url'] as String?, + mirrorUrl: json['mirror_url'] as String?, + name: json['name'] as String?, + networkCount: (json['network_count'] as num?)?.toInt(), + nodeId: json['node_id'] as String?, + notificationsUrl: json['notifications_url'] as String?, + openIssuesCount: (json['open_issues_count'] as num?)?.toInt(), + owner: json['owner'] == null + ? null + : Owner.fromJson(json['owner'] as Map), + permissions: json['permissions'] == null + ? null + : Permissions.fromJson(json['permissions'] as Map), + private: json['private'] as bool?, + pullsUrl: json['pulls_url'] as String?, + pushedAt: json['pushed_at'] == null + ? null + : DateTime.parse(json['pushed_at'] as String), + releasesUrl: json['releases_url'] as String?, + size: (json['size'] as num?)?.toInt(), + squashMergeCommitMessage: json['squash_merge_commit_message'] as String?, + squashMergeCommitTitle: json['squash_merge_commit_title'] as String?, + sshUrl: json['ssh_url'] as String?, + stargazersCount: (json['stargazers_count'] as num?)?.toInt(), + stargazersUrl: json['stargazers_url'] as String?, + statusesUrl: json['statuses_url'] as String?, + subscribersCount: (json['subscribers_count'] as num?)?.toInt(), + subscribersUrl: json['subscribers_url'] as String?, + subscriptionUrl: json['subscription_url'] as String?, + svnUrl: json['svn_url'] as String?, + tagsUrl: json['tags_url'] as String?, + teamsUrl: json['teams_url'] as String?, + tempCloneToken: json['temp_clone_token'] as String?, + topics: + (json['topics'] as List?)?.map((e) => e as String).toList(), + treesUrl: json['trees_url'] as String?, + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + url: json['url'] as String?, + visibility: json['visibility'] as String?, + watchersCount: (json['watchers_count'] as num?)?.toInt(), + ); + +Map _$TemplateRepositoryToJson(TemplateRepository instance) => + { + 'allow_auto_merge': instance.allowAutoMerge, + 'allow_merge_commit': instance.allowMergeCommit, + 'allow_rebase_merge': instance.allowRebaseMerge, + 'allow_squash_merge': instance.allowSquashMerge, + 'allow_update_branch': instance.allowUpdateBranch, + 'archive_url': instance.archiveUrl, + 'archived': instance.archived, + 'assignees_url': instance.assigneesUrl, + 'blobs_url': instance.blobsUrl, + 'branches_url': instance.branchesUrl, + 'clone_url': instance.cloneUrl, + 'collaborators_url': instance.collaboratorsUrl, + 'comments_url': instance.commentsUrl, + 'commits_url': instance.commitsUrl, + 'compare_url': instance.compareUrl, + 'contents_url': instance.contentsUrl, + 'contributors_url': instance.contributorsUrl, + 'created_at': instance.createdAt?.toIso8601String(), + 'default_branch': instance.defaultBranch, + 'delete_branch_on_merge': instance.deleteBranchOnMerge, + 'deployments_url': instance.deploymentsUrl, + 'description': instance.description, + 'disabled': instance.disabled, + 'downloads_url': instance.downloadsUrl, + 'events_url': instance.eventsUrl, + 'fork': instance.fork, + 'forks_count': instance.forksCount, + 'forks_url': instance.forksUrl, + 'full_name': instance.fullName, + 'git_commits_url': instance.gitCommitsUrl, + 'git_refs_url': instance.gitRefsUrl, + 'git_tags_url': instance.gitTagsUrl, + 'git_url': instance.gitUrl, + 'has_downloads': instance.hasDownloads, + 'has_issues': instance.hasIssues, + 'has_pages': instance.hasPages, + 'has_projects': instance.hasProjects, + 'has_wiki': instance.hasWiki, + 'homepage': instance.homepage, + 'hooks_url': instance.hooksUrl, + 'html_url': instance.htmlUrl, + 'id': instance.id, + 'is_template': instance.isTemplate, + 'issue_comment_url': instance.issueCommentUrl, + 'issue_events_url': instance.issueEventsUrl, + 'issues_url': instance.issuesUrl, + 'keys_url': instance.keysUrl, + 'labels_url': instance.labelsUrl, + 'language': instance.language, + 'languages_url': instance.languagesUrl, + 'merge_commit_message': instance.mergeCommitMessage, + 'merge_commit_title': instance.mergeCommitTitle, + 'merges_url': instance.mergesUrl, + 'milestones_url': instance.milestonesUrl, + 'mirror_url': instance.mirrorUrl, + 'name': instance.name, + 'network_count': instance.networkCount, + 'node_id': instance.nodeId, + 'notifications_url': instance.notificationsUrl, + 'open_issues_count': instance.openIssuesCount, + 'owner': instance.owner, + 'permissions': instance.permissions, + 'private': instance.private, + 'pulls_url': instance.pullsUrl, + 'pushed_at': instance.pushedAt?.toIso8601String(), + 'releases_url': instance.releasesUrl, + 'size': instance.size, + 'squash_merge_commit_message': instance.squashMergeCommitMessage, + 'squash_merge_commit_title': instance.squashMergeCommitTitle, + 'ssh_url': instance.sshUrl, + 'stargazers_count': instance.stargazersCount, + 'stargazers_url': instance.stargazersUrl, + 'statuses_url': instance.statusesUrl, + 'subscribers_count': instance.subscribersCount, + 'subscribers_url': instance.subscribersUrl, + 'subscription_url': instance.subscriptionUrl, + 'svn_url': instance.svnUrl, + 'tags_url': instance.tagsUrl, + 'teams_url': instance.teamsUrl, + 'temp_clone_token': instance.tempCloneToken, + 'topics': instance.topics, + 'trees_url': instance.treesUrl, + 'updated_at': instance.updatedAt?.toIso8601String(), + 'url': instance.url, + 'visibility': instance.visibility, + 'watchers_count': instance.watchersCount, + }; + +Owner _$OwnerFromJson(Map json) => Owner( + avatarUrl: json['avatar_url'] as String?, + eventsUrl: json['events_url'] as String?, + followersUrl: json['followers_url'] as String?, + followingUrl: json['following_url'] as String?, + gistsUrl: json['gists_url'] as String?, + gravatarId: json['gravatar_id'] as String?, + htmlUrl: json['html_url'] as String?, + id: (json['id'] as num?)?.toInt(), + login: json['login'] as String?, + nodeId: json['node_id'] as String?, + organizationsUrl: json['organizations_url'] as String?, + receivedEventsUrl: json['received_events_url'] as String?, + reposUrl: json['repos_url'] as String?, + siteAdmin: json['site_admin'] as bool?, + starredUrl: json['starred_url'] as String?, + subscriptionsUrl: json['subscriptions_url'] as String?, + type: json['type'] as String?, + url: json['url'] as String?, + ); + +Map _$OwnerToJson(Owner instance) => { + 'avatar_url': instance.avatarUrl, + 'events_url': instance.eventsUrl, + 'followers_url': instance.followersUrl, + 'following_url': instance.followingUrl, + 'gists_url': instance.gistsUrl, + 'gravatar_id': instance.gravatarId, + 'html_url': instance.htmlUrl, + 'id': instance.id, + 'login': instance.login, + 'node_id': instance.nodeId, + 'organizations_url': instance.organizationsUrl, + 'received_events_url': instance.receivedEventsUrl, + 'repos_url': instance.reposUrl, + 'site_admin': instance.siteAdmin, + 'starred_url': instance.starredUrl, + 'subscriptions_url': instance.subscriptionsUrl, + 'type': instance.type, + 'url': instance.url, + }; + +Tree _$TreeFromJson(Map json) => Tree( + sha: json['sha'] as String?, + url: json['url'] as String?, + htmlUrl: json['html_url'] as String?, + ); + +Map _$TreeToJson(Tree instance) => { + 'sha': instance.sha, + 'url': instance.url, + 'html_url': instance.htmlUrl, + }; + +Verification _$VerificationFromJson(Map json) => Verification( + payload: json['payload'] as String?, + reason: json['reason'] as String?, + signature: json['signature'] as String?, + verified: json['verified'] as bool?, + ); + +Map _$VerificationToJson(Verification instance) => + { + 'payload': instance.payload, + 'reason': instance.reason, + 'signature': instance.signature, + 'verified': instance.verified, + }; + +HtmlLink _$HtmlLinkFromJson(Map json) => HtmlLink( + href: json['href'] as String?, + ); + +Map _$HtmlLinkToJson(HtmlLink instance) => { + 'href': instance.href, + }; + +PullRequestLink _$PullRequestLinkFromJson(Map json) => + PullRequestLink( + href: json['href'] as String?, + ); + +Map _$PullRequestLinkToJson(PullRequestLink instance) => + { + 'href': instance.href, + }; diff --git a/lib/src/common/model/users.dart b/lib/src/common/model/users.dart index b616bd3b..f66fd395 100644 --- a/lib/src/common/model/users.dart +++ b/lib/src/common/model/users.dart @@ -3,7 +3,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'users.g.dart'; /// Model class for a user. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class User { User({ this.id, @@ -24,71 +24,130 @@ class User { this.followingCount, this.createdAt, this.updatedAt, + + // Properties from the Timeline API + this.eventsUrl, + this.followersUrl, + this.followingUrl, + this.gistsUrl, + this.gravatarId, + this.nodeId, + this.organizationsUrl, + this.receivedEventsUrl, + this.reposUrl, + this.starredAt, + this.starredUrl, + this.subscriptionsUrl, + this.type, + this.url, }); - @JsonKey(ignore: true) - Map json; // TODO remove + @JsonKey(includeToJson: false, includeFromJson: false) + Map? json; // TODO remove /// User's Username - String login; + String? login; /// User ID - int id; + int? id; /// Avatar URL - String avatarUrl; + String? avatarUrl; /// Url to this user's profile. - String htmlUrl; + String? htmlUrl; /// If the user is a site administrator - bool siteAdmin; + bool? siteAdmin; /// User's Name - String name; + String? name; /// Name of User's Company - String company; + String? company; /// Link to User's Blog - String blog; + String? blog; /// User's Location - String location; + String? location; /// User's Email - String email; + String? email; /// If this user is hirable - bool hirable; + bool? hirable; /// The User's Biography - String bio; + String? bio; /// Number of public repositories that this user has @JsonKey(name: 'public_repos') - int publicReposCount; + int? publicReposCount; /// Number of public gists that this user has @JsonKey(name: 'public_gists') - int publicGistsCount; + int? publicGistsCount; /// Number of followers that this user has @JsonKey(name: 'followers') - int followersCount; + int? followersCount; /// Number of Users that this user follows @JsonKey(name: 'following') - int followingCount; + int? followingCount; /// The time this [User] was created. - DateTime createdAt; + DateTime? createdAt; /// Last time this [User] was updated. - DateTime updatedAt; + DateTime? updatedAt; /// The username of the twitter account (without leading @) - String twitterUsername; + String? twitterUsername; + + // The following properties were added to support the Timeline API. + + /// Example: `https://api.github.com/users/octocat/events{/privacy}` + String? eventsUrl; + + /// Example: `https://api.github.com/users/octocat/followers` + String? followersUrl; + + /// Example: `https://api.github.com/users/octocat/following{/other_user}` + String? followingUrl; + + /// Example: `https://api.github.com/users/octocat/gists{/gist_id}` + String? gistsUrl; + + /// Example: `41d064eb2195891e12d0413f63227ea7` + String? gravatarId; + + /// Example: `MDQ6VXNlcjE=` + String? nodeId; + + /// Example: `https://api.github.com/users/octocat/orgs` + String? organizationsUrl; + + /// Example: `https://api.github.com/users/octocat/received_events` + String? receivedEventsUrl; + + /// Example: `https://api.github.com/users/octocat/repos` + String? reposUrl; + + DateTime? starredAt; + + /// Example: `https://api.github.com/users/octocat/starred{/owner}{/repo}` + String? starredUrl; + + /// Example: `https://api.github.com/users/octocat/subscriptions` + String? subscriptionsUrl; + + /// Example: `User` + String? type; + + /// Example: `https://api.github.com/users/octocat` + String? url; factory User.fromJson(Map input) => _$UserFromJson(input); Map toJson() => _$UserToJson(this); @@ -96,15 +155,8 @@ class User { /// The response from listing collaborators on a repo. // https://developer.github.com/v3/repos/collaborators/#response -@JsonSerializable(createToJson: false, fieldRename: FieldRename.snake) +@JsonSerializable() class Collaborator { - final String login; - final int id; - final String htmlUrl; - final String type; - final bool siteAdmin; - final Map permissions; - Collaborator( this.login, this.id, @@ -114,14 +166,22 @@ class Collaborator { this.permissions, ); + String? login; + int? id; + String? htmlUrl; + String? type; + bool? siteAdmin; + Map? permissions; + factory Collaborator.fromJson(Map json) => _$CollaboratorFromJson(json); + Map toJson() => _$CollaboratorToJson(this); } /// The response from listing contributors on a repo. /// /// https://developer.github.com/v3/repos/#response-if-repository-contains-content -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class Contributor { Contributor({ this.id, @@ -134,24 +194,24 @@ class Contributor { }); /// User's Username - String login; + String? login; /// User ID - int id; + int? id; /// Avatar URL - String avatarUrl; + String? avatarUrl; /// Url to this user's profile. - String htmlUrl; + String? htmlUrl; - String type; + String? type; /// If the user is a site administrator - bool siteAdmin; + bool? siteAdmin; /// Contributions count - int contributions; + int? contributions; factory Contributor.fromJson(Map input) => _$ContributorFromJson(input); @@ -159,24 +219,24 @@ class Contributor { } /// The Currently Authenticated User -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class CurrentUser extends User { CurrentUser(); /// Number of Private Repositories @JsonKey(name: 'total_private_repos') - int privateReposCount; + int? privateReposCount; /// Number of Owned Private Repositories that the user owns @JsonKey(name: 'owned_private_repos') - int ownedPrivateReposCount; + int? ownedPrivateReposCount; /// The User's Disk Usage @JsonKey(name: 'disk_usage') - int diskUsage; + int? diskUsage; /// The User's GitHub Plan - UserPlan plan; + UserPlan? plan; factory CurrentUser.fromJson(Map input) => _$CurrentUserFromJson(input); @@ -185,23 +245,23 @@ class CurrentUser extends User { } /// A Users GitHub Plan -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class UserPlan { UserPlan(); // Plan Name - String name; + String? name; // Plan Space - int space; + int? space; // Number of Private Repositories @JsonKey(name: 'private_repos') - int privateReposCount; + int? privateReposCount; // Number of Collaborators @JsonKey(name: 'collaborators') - int collaboratorsCount; + int? collaboratorsCount; factory UserPlan.fromJson(Map input) => _$UserPlanFromJson(input); @@ -209,16 +269,16 @@ class UserPlan { } /// Model class for a user's email address. -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class UserEmail { UserEmail({ this.email, this.verified, this.primary, }); - String email; - bool verified; - bool primary; + String? email; + bool? verified; + bool? primary; factory UserEmail.fromJson(Map input) => _$UserEmailFromJson(input); diff --git a/lib/src/common/model/users.g.dart b/lib/src/common/model/users.g.dart index 57a69716..1e61153c 100644 --- a/lib/src/common/model/users.g.dart +++ b/lib/src/common/model/users.g.dart @@ -6,32 +6,46 @@ part of 'users.dart'; // JsonSerializableGenerator // ************************************************************************** -User _$UserFromJson(Map json) { - return User( - id: json['id'] as int, - login: json['login'] as String, - avatarUrl: json['avatar_url'] as String, - htmlUrl: json['html_url'] as String, - siteAdmin: json['site_admin'] as bool, - name: json['name'] as String, - company: json['company'] as String, - blog: json['blog'] as String, - location: json['location'] as String, - email: json['email'] as String, - hirable: json['hirable'] as bool, - bio: json['bio'] as String, - publicReposCount: json['public_repos'] as int, - publicGistsCount: json['public_gists'] as int, - followersCount: json['followers'] as int, - followingCount: json['following'] as int, - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - )..twitterUsername = json['twitter_username'] as String; -} +User _$UserFromJson(Map json) => User( + id: (json['id'] as num?)?.toInt(), + login: json['login'] as String?, + avatarUrl: json['avatar_url'] as String?, + htmlUrl: json['html_url'] as String?, + siteAdmin: json['site_admin'] as bool?, + name: json['name'] as String?, + company: json['company'] as String?, + blog: json['blog'] as String?, + location: json['location'] as String?, + email: json['email'] as String?, + hirable: json['hirable'] as bool?, + bio: json['bio'] as String?, + publicReposCount: (json['public_repos'] as num?)?.toInt(), + publicGistsCount: (json['public_gists'] as num?)?.toInt(), + followersCount: (json['followers'] as num?)?.toInt(), + followingCount: (json['following'] as num?)?.toInt(), + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + eventsUrl: json['events_url'] as String?, + followersUrl: json['followers_url'] as String?, + followingUrl: json['following_url'] as String?, + gistsUrl: json['gists_url'] as String?, + gravatarId: json['gravatar_id'] as String?, + nodeId: json['node_id'] as String?, + organizationsUrl: json['organizations_url'] as String?, + receivedEventsUrl: json['received_events_url'] as String?, + reposUrl: json['repos_url'] as String?, + starredAt: json['starred_at'] == null + ? null + : DateTime.parse(json['starred_at'] as String), + starredUrl: json['starred_url'] as String?, + subscriptionsUrl: json['subscriptions_url'] as String?, + type: json['type'] as String?, + url: json['url'] as String?, + )..twitterUsername = json['twitter_username'] as String?; Map _$UserToJson(User instance) => { 'login': instance.login, @@ -53,32 +67,52 @@ Map _$UserToJson(User instance) => { 'created_at': instance.createdAt?.toIso8601String(), 'updated_at': instance.updatedAt?.toIso8601String(), 'twitter_username': instance.twitterUsername, + 'events_url': instance.eventsUrl, + 'followers_url': instance.followersUrl, + 'following_url': instance.followingUrl, + 'gists_url': instance.gistsUrl, + 'gravatar_id': instance.gravatarId, + 'node_id': instance.nodeId, + 'organizations_url': instance.organizationsUrl, + 'received_events_url': instance.receivedEventsUrl, + 'repos_url': instance.reposUrl, + 'starred_at': instance.starredAt?.toIso8601String(), + 'starred_url': instance.starredUrl, + 'subscriptions_url': instance.subscriptionsUrl, + 'type': instance.type, + 'url': instance.url, }; -Collaborator _$CollaboratorFromJson(Map json) { - return Collaborator( - json['login'] as String, - json['id'] as int, - json['html_url'] as String, - json['type'] as String, - json['site_admin'] as bool, - (json['permissions'] as Map)?.map( - (k, e) => MapEntry(k, e as bool), - ), - ); -} +Collaborator _$CollaboratorFromJson(Map json) => Collaborator( + json['login'] as String?, + (json['id'] as num?)?.toInt(), + json['html_url'] as String?, + json['type'] as String?, + json['site_admin'] as bool?, + (json['permissions'] as Map?)?.map( + (k, e) => MapEntry(k, e as bool), + ), + ); + +Map _$CollaboratorToJson(Collaborator instance) => + { + 'login': instance.login, + 'id': instance.id, + 'html_url': instance.htmlUrl, + 'type': instance.type, + 'site_admin': instance.siteAdmin, + 'permissions': instance.permissions, + }; -Contributor _$ContributorFromJson(Map json) { - return Contributor( - id: json['id'] as int, - login: json['login'] as String, - avatarUrl: json['avatar_url'] as String, - htmlUrl: json['html_url'] as String, - type: json['type'] as String, - siteAdmin: json['site_admin'] as bool, - contributions: json['contributions'] as int, - ); -} +Contributor _$ContributorFromJson(Map json) => Contributor( + id: (json['id'] as num?)?.toInt(), + login: json['login'] as String?, + avatarUrl: json['avatar_url'] as String?, + htmlUrl: json['html_url'] as String?, + type: json['type'] as String?, + siteAdmin: json['site_admin'] as bool?, + contributions: (json['contributions'] as num?)?.toInt(), + ); Map _$ContributorToJson(Contributor instance) => { @@ -91,38 +125,52 @@ Map _$ContributorToJson(Contributor instance) => 'contributions': instance.contributions, }; -CurrentUser _$CurrentUserFromJson(Map json) { - return CurrentUser() - ..login = json['login'] as String - ..id = json['id'] as int - ..avatarUrl = json['avatar_url'] as String - ..htmlUrl = json['html_url'] as String - ..siteAdmin = json['site_admin'] as bool - ..name = json['name'] as String - ..company = json['company'] as String - ..blog = json['blog'] as String - ..location = json['location'] as String - ..email = json['email'] as String - ..hirable = json['hirable'] as bool - ..bio = json['bio'] as String - ..publicReposCount = json['public_repos'] as int - ..publicGistsCount = json['public_gists'] as int - ..followersCount = json['followers'] as int - ..followingCount = json['following'] as int - ..createdAt = json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String) - ..updatedAt = json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String) - ..twitterUsername = json['twitter_username'] as String - ..privateReposCount = json['total_private_repos'] as int - ..ownedPrivateReposCount = json['owned_private_repos'] as int - ..diskUsage = json['disk_usage'] as int - ..plan = json['plan'] == null - ? null - : UserPlan.fromJson(json['plan'] as Map); -} +CurrentUser _$CurrentUserFromJson(Map json) => CurrentUser() + ..login = json['login'] as String? + ..id = (json['id'] as num?)?.toInt() + ..avatarUrl = json['avatar_url'] as String? + ..htmlUrl = json['html_url'] as String? + ..siteAdmin = json['site_admin'] as bool? + ..name = json['name'] as String? + ..company = json['company'] as String? + ..blog = json['blog'] as String? + ..location = json['location'] as String? + ..email = json['email'] as String? + ..hirable = json['hirable'] as bool? + ..bio = json['bio'] as String? + ..publicReposCount = (json['public_repos'] as num?)?.toInt() + ..publicGistsCount = (json['public_gists'] as num?)?.toInt() + ..followersCount = (json['followers'] as num?)?.toInt() + ..followingCount = (json['following'] as num?)?.toInt() + ..createdAt = json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String) + ..updatedAt = json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String) + ..twitterUsername = json['twitter_username'] as String? + ..eventsUrl = json['events_url'] as String? + ..followersUrl = json['followers_url'] as String? + ..followingUrl = json['following_url'] as String? + ..gistsUrl = json['gists_url'] as String? + ..gravatarId = json['gravatar_id'] as String? + ..nodeId = json['node_id'] as String? + ..organizationsUrl = json['organizations_url'] as String? + ..receivedEventsUrl = json['received_events_url'] as String? + ..reposUrl = json['repos_url'] as String? + ..starredAt = json['starred_at'] == null + ? null + : DateTime.parse(json['starred_at'] as String) + ..starredUrl = json['starred_url'] as String? + ..subscriptionsUrl = json['subscriptions_url'] as String? + ..type = json['type'] as String? + ..url = json['url'] as String? + ..privateReposCount = (json['total_private_repos'] as num?)?.toInt() + ..ownedPrivateReposCount = (json['owned_private_repos'] as num?)?.toInt() + ..diskUsage = (json['disk_usage'] as num?)?.toInt() + ..plan = json['plan'] == null + ? null + : UserPlan.fromJson(json['plan'] as Map); Map _$CurrentUserToJson(CurrentUser instance) => { @@ -145,19 +193,31 @@ Map _$CurrentUserToJson(CurrentUser instance) => 'created_at': instance.createdAt?.toIso8601String(), 'updated_at': instance.updatedAt?.toIso8601String(), 'twitter_username': instance.twitterUsername, + 'events_url': instance.eventsUrl, + 'followers_url': instance.followersUrl, + 'following_url': instance.followingUrl, + 'gists_url': instance.gistsUrl, + 'gravatar_id': instance.gravatarId, + 'node_id': instance.nodeId, + 'organizations_url': instance.organizationsUrl, + 'received_events_url': instance.receivedEventsUrl, + 'repos_url': instance.reposUrl, + 'starred_at': instance.starredAt?.toIso8601String(), + 'starred_url': instance.starredUrl, + 'subscriptions_url': instance.subscriptionsUrl, + 'type': instance.type, + 'url': instance.url, 'total_private_repos': instance.privateReposCount, 'owned_private_repos': instance.ownedPrivateReposCount, 'disk_usage': instance.diskUsage, 'plan': instance.plan, }; -UserPlan _$UserPlanFromJson(Map json) { - return UserPlan() - ..name = json['name'] as String - ..space = json['space'] as int - ..privateReposCount = json['private_repos'] as int - ..collaboratorsCount = json['collaborators'] as int; -} +UserPlan _$UserPlanFromJson(Map json) => UserPlan() + ..name = json['name'] as String? + ..space = (json['space'] as num?)?.toInt() + ..privateReposCount = (json['private_repos'] as num?)?.toInt() + ..collaboratorsCount = (json['collaborators'] as num?)?.toInt(); Map _$UserPlanToJson(UserPlan instance) => { 'name': instance.name, @@ -166,13 +226,11 @@ Map _$UserPlanToJson(UserPlan instance) => { 'collaborators': instance.collaboratorsCount, }; -UserEmail _$UserEmailFromJson(Map json) { - return UserEmail( - email: json['email'] as String, - verified: json['verified'] as bool, - primary: json['primary'] as bool, - ); -} +UserEmail _$UserEmailFromJson(Map json) => UserEmail( + email: json['email'] as String?, + verified: json['verified'] as bool?, + primary: json['primary'] as bool?, + ); Map _$UserEmailToJson(UserEmail instance) => { 'email': instance.email, diff --git a/lib/src/common/orgs_service.dart b/lib/src/common/orgs_service.dart index 3f3dfa6c..b2ca7b26 100644 --- a/lib/src/common/orgs_service.dart +++ b/lib/src/common/orgs_service.dart @@ -1,8 +1,7 @@ import 'dart:async'; import 'dart:convert'; + import 'package:github/src/common.dart'; -import 'package:github/src/common/util/pagination.dart'; -import 'package:github/src/common/util/utils.dart'; import 'package:http/http.dart' as http; /// The [OrganizationsService] handles communication with organization @@ -10,30 +9,32 @@ import 'package:http/http.dart' as http; /// /// API docs: https://developer.github.com/v3/orgs/ class OrganizationsService extends Service { - OrganizationsService(GitHub github) : super(github); + OrganizationsService(super.github); /// Lists all of the memberships in organizations for the given [userName]. /// If [userName] is not specified we list the memberships in organizations /// for the authenticated user. /// /// API docs: : https://developer.github.com/v3/orgs/#list-user-organizations - Stream list([String userName]) { + Stream list([String? userName]) { var requestPath = '/users/$userName/orgs'; if (userName == null) { requestPath = '/user/orgs'; } - return PaginationHelper(github) - .objects('GET', requestPath, (i) => Organization.fromJson(i)); + return PaginationHelper(github).objects( + 'GET', + requestPath, + Organization.fromJson, + ); } /// Fetches the organization specified by [name]. /// /// API docs: https://developer.github.com/v3/orgs/#get-an-organization - Future get(String name) => github.getJSON('/orgs/$name', - convert: (i) => Organization.fromJson(i), - statusCode: StatusCodes.OK, - fail: (http.Response response) { - if (response.statusCode == 404) { + Future get(String? name) => github.getJSON('/orgs/$name', + convert: Organization.fromJson, + statusCode: StatusCodes.OK, fail: (http.Response response) { + if (response.statusCode == StatusCodes.NOT_FOUND) { throw OrganizationNotFound(github, name); } }); @@ -49,13 +50,15 @@ class OrganizationsService extends Service { /// Edits an Organization /// /// API docs: https://developer.github.com/v3/orgs/#edit-an-organization - Future edit(String org, - {String billingEmail, - String company, - String email, - String location, - String name, - String description}) { + Future edit( + String org, { + String? billingEmail, + String? company, + String? email, + String? location, + String? name, + String? description, + }) { final map = createNonNullMap({ 'billing_email': billingEmail, 'company': company, @@ -66,8 +69,8 @@ class OrganizationsService extends Service { }); return github.postJSON('/orgs/$org', - statusCode: 200, - convert: (i) => Organization.fromJson(i), + statusCode: StatusCodes.OK, + convert: Organization.fromJson, body: GitHubJson.encode(map)); } @@ -75,8 +78,11 @@ class OrganizationsService extends Service { /// /// API docs: https://developer.github.com/v3/orgs/teams/#list-teams Stream listTeams(String orgName) { - return PaginationHelper(github) - .objects('GET', '/orgs/$orgName/teams', (i) => Team.fromJson(i)); + return PaginationHelper(github).objects( + 'GET', + '/orgs/$orgName/teams', + Team.fromJson, + ); } /// Gets the team specified by the [teamId]. @@ -84,15 +90,34 @@ class OrganizationsService extends Service { /// API docs: https://developer.github.com/v3/orgs/teams/#get-team Future getTeam(int teamId) { return github.getJSON('/teams/$teamId', - convert: (i) => Organization.fromJson(i), - statusCode: 200) as Future; + convert: Organization.fromJson, + statusCode: StatusCodes.OK) as Future; + } + + /// Gets the team specified by its [teamName]. + /// + /// https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#get-a-team-by-name + Future getTeamByName( + String orgName, + String teamName, + ) { + return github.getJSON( + 'orgs/$orgName/teams/$teamName', + convert: Team.fromJson, + statusCode: StatusCodes.OK, + ); } /// Creates a Team. /// /// API docs: https://developer.github.com/v3/orgs/teams/#create-team - Future createTeam(String org, String name, - {String description, List repos, String permission}) { + Future createTeam( + String org, + String name, { + String? description, + List? repos, + String? permission, + }) { final map = createNonNullMap({ 'name': name, 'description': description, @@ -101,16 +126,20 @@ class OrganizationsService extends Service { }); return github.postJSON('/orgs/$org/teams', - statusCode: 201, - convert: (i) => Team.fromJson(i), + statusCode: StatusCodes.CREATED, + convert: Team.fromJson, body: GitHubJson.encode(map)); } /// Edits a Team. /// /// API docs: https://developer.github.com/v3/orgs/teams/#edit-team - Future editTeam(int teamId, String name, - {String description, String permission}) { + Future editTeam( + int teamId, + String name, { + String? description, + String? permission, + }) { final map = createNonNullMap({ 'name': name, 'description': description, @@ -119,8 +148,8 @@ class OrganizationsService extends Service { return github.postJSON( '/teams/$teamId', - statusCode: 200, - convert: (i) => Team.fromJson(i), + statusCode: StatusCodes.OK, + convert: Team.fromJson, body: GitHubJson.encode(map), ); } @@ -130,7 +159,7 @@ class OrganizationsService extends Service { /// API docs: https://developer.github.com/v3/orgs/teams/#delete-team Future deleteTeam(int teamId) { return github.request('DELETE', '/teams/$teamId').then((response) { - return response.statusCode == 204; + return response.statusCode == StatusCodes.NO_CONTENT; }); } @@ -139,7 +168,10 @@ class OrganizationsService extends Service { /// API docs: https://developer.github.com/v3/orgs/teams/#list-team-members Stream listTeamMembers(int teamId) { return PaginationHelper(github).objects( - 'GET', '/teams/$teamId/members', (i) => TeamMember.fromJson(i)); + 'GET', + '/teams/$teamId/members', + TeamMember.fromJson, + ); } Future getTeamMemberStatus(int teamId, String user) { @@ -148,85 +180,129 @@ class OrganizationsService extends Service { }); } - /// Returns the membership status for a user in a team. + /// Returns the membership status for a [user] in a team with [teamId]. /// /// API docs: https://developer.github.com/v3/orgs/teams/#get-team-membership - Future getTeamMembership(int teamId, String user) { - final completer = Completer(); - - github - .getJSON( - '/teams/$teamId/memberships/$user', - statusCode: 200, - fail: (http.Response response) { - if (response.statusCode == 404) { - completer.complete(TeamMembershipState(null)); - } else { - github.handleStatusCode(response); - } - }, - convert: (json) => TeamMembershipState(json['state']), - ) - .then(completer.complete); + Future getTeamMembership( + int teamId, + String user, + ) { + return github.getJSON( + '/teams/$teamId/memberships/$user', + statusCode: StatusCodes.OK, + convert: (dynamic json) => TeamMembershipState( + json['state'], + ), + ); + } - return completer.future; + /// Returns the membership status for [user] in [teamName] given the [orgName]. + /// + /// Note that this will throw on NotFound if the user is not a member of the + /// team. Adding a fail function to set the value does not help unless you + /// throw out of the fail function. + Future getTeamMembershipByName( + String orgName, + String teamName, + String user, + ) { + return github.getJSON( + '/orgs/$orgName/teams/$teamName/memberships/$user', + statusCode: StatusCodes.OK, + convert: (dynamic json) => TeamMembershipState( + json['state'], + ), + ); } /// Invites a user to the specified team. /// /// API docs: https://developer.github.com/v3/teams/members/#add-or-update-team-membership - Future addTeamMembership(int teamId, String user) async { - final response = await github - .request('PUT', '/teams/$teamId/memberships/$user', statusCode: 200); + Future addTeamMembership( + int teamId, + String user, + ) async { + final response = await github.request( + 'PUT', + '/teams/$teamId/memberships/$user', + statusCode: StatusCodes.OK, + ); return TeamMembershipState(jsonDecode(response.body)['state']); } /// Removes a user from the specified team. /// /// API docs: https://developer.github.com/v3/orgs/teams/#get-team-membership - Future removeTeamMembership(int teamId, String user) { - return github.request('DELETE', '/teams/$teamId/memberships/$user', - statusCode: 204); + Future removeTeamMembership( + int teamId, + String user, + ) { + return github.request( + 'DELETE', + '/teams/$teamId/memberships/$user', + statusCode: StatusCodes.NO_CONTENT, + ); } /// Lists the repositories that the specified team has access to. /// /// API docs: https://developer.github.com/v3/orgs/teams/#list-team-repos Stream listTeamRepositories(int teamId) { - return PaginationHelper(github) - .objects('GET', '/teams/$teamId/repos', (i) => Repository.fromJson(i)); + return PaginationHelper(github).objects( + 'GET', + '/teams/$teamId/repos', + Repository.fromJson, + ); } /// Checks if a team manages the specified repository. /// /// API docs: https://developer.github.com/v3/orgs/teams/#get-team-repo - Future isTeamRepository(int teamId, RepositorySlug slug) { + Future isTeamRepository( + int teamId, + RepositorySlug slug, + ) { return github - .request('GET', '/teams/$teamId/repos/${slug.fullName}') + .request( + 'GET', + '/teams/$teamId/repos/${slug.fullName}', + ) .then((response) { - return response.statusCode == 204; + return response.statusCode == StatusCodes.NO_CONTENT; }); } /// Adds a repository to be managed by the specified team. /// /// API docs: https://developer.github.com/v3/orgs/teams/#add-team-repo - Future addTeamRepository(int teamId, RepositorySlug slug) { + Future addTeamRepository( + int teamId, + RepositorySlug slug, + ) { return github - .request('PUT', '/teams/$teamId/repos/${slug.fullName}') + .request( + 'PUT', + '/teams/$teamId/repos/${slug.fullName}', + ) .then((response) { - return response.statusCode == 204; + return response.statusCode == StatusCodes.NO_CONTENT; }); } /// Removes a repository from being managed by the specified team. /// /// API docs: https://developer.github.com/v3/orgs/teams/#remove-team-repo - Future removeTeamRepository(int teamId, RepositorySlug slug) { + Future removeTeamRepository( + int teamId, + RepositorySlug slug, + ) { return github - .request('DELETE', '/teams/$teamId/repos/${slug.fullName}') + .request( + 'DELETE', + '/teams/$teamId/repos/${slug.fullName}', + ) .then((response) { - return response.statusCode == 204; + return response.statusCode == StatusCodes.NO_CONTENT; }); } @@ -234,29 +310,49 @@ class OrganizationsService extends Service { /// /// API docs: https://developer.github.com/v3/orgs/teams/#list-user-teams Stream listUserTeams() { - return PaginationHelper(github) - .objects('GET', '/user/teams', (i) => Team.fromJson(i)); + return PaginationHelper(github).objects( + 'GET', + '/user/teams', + Team.fromJson, + ); + } + + /// Lists all of the users in an organization + /// + /// API docs: https://developer.github.com/v3/orgs/teams/#list-user-teams + Stream listUsers(String org) { + return PaginationHelper(github).objects( + 'GET', + '/orgs/$org/members', + User.fromJson, + ); } /// Lists the hooks for the specified organization. /// /// API docs: https://developer.github.com/v3/orgs/hooks/#list-hooks Stream listHooks(String org) { - return PaginationHelper(github).objects( - 'GET', '/orgs/$org/hooks', (i) => Hook.fromJson(i)..repoName = org); + return PaginationHelper(github).objects('GET', '/orgs/$org/hooks', + (dynamic i) => Hook.fromJson(i)..repoName = org); } /// Fetches a single hook by [id]. /// /// API docs: https://developer.github.com/v3/orgs/hooks/#get-single-hook - Future getHook(String org, int id) => + Future getHook( + String org, + int id, + ) => github.getJSON('/orgs/$org/hooks/$id', - convert: (i) => Hook.fromJson(i)..repoName = org); + convert: (dynamic i) => Hook.fromJson(i)..repoName = org); /// Creates an organization hook based on the specified [hook]. /// /// API docs: https://developer.github.com/v3/orgs/hooks/#create-a-hook - Future createHook(String org, CreateHook hook) { + Future createHook( + String org, + CreateHook hook, + ) { return github.postJSON('/orgs/$org/hooks', convert: (Map i) => Hook.fromJson(i)..repoName = org, body: GitHubJson.encode(hook)); @@ -267,16 +363,27 @@ class OrganizationsService extends Service { /// Pings the organization hook. /// /// API docs: https://developer.github.com/v3/orgs/hooks/#ping-a-hook - Future pingHook(String org, int id) { + Future pingHook( + String org, + int id, + ) { return github - .request('POST', '/orgs/$org/hooks/$id/pings') - .then((response) => response.statusCode == 204); + .request( + 'POST', + '/orgs/$org/hooks/$id/pings', + ) + .then((response) => response.statusCode == StatusCodes.NO_CONTENT); } /// Deletes the specified hook. Future deleteHook(String org, int id) { - return github.request('DELETE', '/orgs/$org/hooks/$id').then((response) { - return response.statusCode == 204; + return github + .request( + 'DELETE', + '/orgs/$org/hooks/$id', + ) + .then((response) { + return response.statusCode == StatusCodes.NO_CONTENT; }); } } diff --git a/lib/src/common/pulls_service.dart b/lib/src/common/pulls_service.dart index ddc51b22..7fee2bf9 100644 --- a/lib/src/common/pulls_service.dart +++ b/lib/src/common/pulls_service.dart @@ -1,25 +1,24 @@ import 'dart:async'; import 'dart:convert'; + import 'package:github/src/common.dart'; -import 'package:github/src/common/util/pagination.dart'; -import 'package:github/src/common/util/utils.dart'; /// The [PullRequestsService] handles communication with pull request /// methods of the GitHub API. /// /// API docs: https://developer.github.com/v3/pulls/ class PullRequestsService extends Service { - PullRequestsService(GitHub github) : super(github); + PullRequestsService(super.github); /// Fetches several pull requests. /// /// API docs: https://developer.github.com/v3/pulls/#list-pull-requests Stream list( RepositorySlug slug, { - int pages, - String base, + int? pages, + String? base, String direction = 'desc', - String head, + String? head, String sort = 'created', String state = 'open', }) { @@ -31,11 +30,8 @@ class PullRequestsService extends Service { putValue('state', state, params); return PaginationHelper(github).objects( - 'GET', - '/repos/${slug.fullName}/pulls?state=$state', - (i) => PullRequest.fromJson(i), - pages: pages, - params: params); + 'GET', '/repos/${slug.fullName}/pulls', PullRequest.fromJson, + pages: pages, params: params); } /// Fetches a single pull request. @@ -43,7 +39,7 @@ class PullRequestsService extends Service { /// API docs: https://developer.github.com/v3/pulls/#get-a-single-pull-request Future get(RepositorySlug slug, int number) => github.getJSON('/repos/${slug.fullName}/pulls/$number', - convert: (i) => PullRequest.fromJson(i), statusCode: StatusCodes.OK); + convert: PullRequest.fromJson, statusCode: StatusCodes.OK); /// Creates a Pull Request based on the given [request]. /// @@ -51,9 +47,9 @@ class PullRequestsService extends Service { Future create(RepositorySlug slug, CreatePullRequest request) { return github.postJSON( '/repos/${slug.fullName}/pulls', - convert: (i) => PullRequest.fromJson(i), + convert: PullRequest.fromJson, body: GitHubJson.encode(request), - preview: request.draft + preview: request.draft! ? 'application/vnd.github.shadow-cat-preview+json' : null, ); @@ -63,7 +59,7 @@ class PullRequestsService extends Service { /// /// API docs: https://developer.github.com/v3/pulls/#update-a-pull-request Future edit(RepositorySlug slug, int number, - {String title, String body, String state, String base}) { + {String? title, String? body, String? state, String? base}) { final map = {}; putValue('title', title, map); putValue('body', body, map); @@ -86,7 +82,7 @@ class PullRequestsService extends Service { return PaginationHelper(github).objects( 'GET', '/repos/${slug.fullName}/pulls/$number/commits', - (i) => RepositoryCommit.fromJson(i)); + RepositoryCommit.fromJson); } /// Lists the files in a pull request. @@ -96,7 +92,17 @@ class PullRequestsService extends Service { return PaginationHelper(github).objects( 'GET', '/repos/${slug.fullName}/pulls/$number/files', - (i) => PullRequestFile.fromJson(i)); + PullRequestFile.fromJson); + } + + /// Lists the reviews for a pull request. + /// + /// API docs: https://docs.github.com/en/rest/reference/pulls#list-reviews-for-a-pull-request + Stream listReviews(RepositorySlug slug, int number) { + return PaginationHelper(github).objects( + 'GET', + '/repos/${slug.fullName}/pulls/$number/reviews', + PullRequestReview.fromJson); } Future isMerged(RepositorySlug slug, int number) { @@ -113,17 +119,28 @@ class PullRequestsService extends Service { Future merge( RepositorySlug slug, int number, { - String message, + String? message, + MergeMethod mergeMethod = MergeMethod.merge, + String? requestSha, }) { final json = {}; if (message != null) { json['commit_message'] = message; } + if (requestSha != null) { + json['sha'] = requestSha; + } + + json['merge_method'] = mergeMethod.name; + + // Recommended Accept header when making a merge request. + Map? headers = {}; + headers['Accept'] = 'application/vnd.github+json'; return github .request('PUT', '/repos/${slug.fullName}/pulls/$number/merge', - body: GitHubJson.encode(json)) + headers: headers, body: GitHubJson.encode(json)) .then((response) { return PullRequestMerge.fromJson( jsonDecode(response.body) as Map); @@ -138,30 +155,46 @@ class PullRequestsService extends Service { return PaginationHelper(github).objects( 'GET', '/repos/${slug.fullName}/pulls/$number/comments', - (i) => PullRequestComment.fromJson(i)); + PullRequestComment.fromJson); } /// Lists all comments on all pull requests for the repository. /// /// API docs: https://developer.github.com/v3/pulls/comments/#list-comments-in-a-repository Stream listComments(RepositorySlug slug) { - return PaginationHelper(github).objects( - 'GET', - '/repos/${slug.fullName}/pulls/comments', - (i) => PullRequestComment.fromJson(i)); + return PaginationHelper(github).objects('GET', + '/repos/${slug.fullName}/pulls/comments', PullRequestComment.fromJson); } /// Creates a new pull request comment. /// /// API docs: https://developer.github.com/v3/pulls/comments/#create-a-comment - Future createComment( + Future createComment( RepositorySlug slug, int number, CreatePullRequestComment comment) { return github.postJSON('/repos/${slug.fullName}/pulls/$number/comments', body: GitHubJson.encode(comment.toJson()), - convert: (i) => PullRequestComment.fromJson(i), - statusCode: 201) as Future; + convert: PullRequestComment.fromJson, + statusCode: 201); } // TODO: Implement editComment: https://developer.github.com/v3/pulls/comments/#edit-a-comment // TODO: Implement deleteComment: https://developer.github.com/v3/pulls/comments/#delete-a-comment + + /// Creates a new pull request comment. + /// + /// API docs: https://developer.github.com/v3/pulls/comments/#create-a-comment + Future createReview( + RepositorySlug slug, CreatePullRequestReview review) { + return github.postJSON( + '/repos/${slug.fullName}/pulls/${review.pullNumber}/reviews', + body: GitHubJson.encode(review), + convert: PullRequestReview.fromJson, + ); + } +} + +enum MergeMethod { + merge, + squash, + rebase, } diff --git a/lib/src/common/repos_service.dart b/lib/src/common/repos_service.dart index ee2860b5..83fa5ef0 100644 --- a/lib/src/common/repos_service.dart +++ b/lib/src/common/repos_service.dart @@ -1,19 +1,15 @@ import 'dart:async'; import 'dart:convert'; + import 'package:github/src/common.dart'; -import 'package:github/src/common/model/repos_releases.dart'; -import 'package:github/src/common/model/users.dart'; -import 'package:github/src/common/util/pagination.dart'; -import 'package:github/src/common/util/utils.dart'; import 'package:http/http.dart' as http; -import 'package:meta/meta.dart'; /// The [RepositoriesService] handles communication with repository related /// methods of the GitHub API. /// /// API docs: https://developer.github.com/v3/repos/ class RepositoriesService extends Service { - RepositoriesService(GitHub github) : super(github); + RepositoriesService(super.github); /// Lists the repositories of the currently authenticated user. /// @@ -31,7 +27,7 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, Repository>( 'GET', '/user/repos', - (i) => Repository.fromJson(i), + Repository.fromJson, params: params, ); } @@ -53,7 +49,7 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, Repository>( 'GET', '/users/$user/repos', - (i) => Repository.fromJson(i), + Repository.fromJson, params: params, ); } @@ -69,7 +65,7 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, Repository>( 'GET', '/orgs/$org/repos', - (i) => Repository.fromJson(i), + Repository.fromJson, params: params, ); } @@ -81,21 +77,21 @@ class RepositoriesService extends Service { /// If [limit] is null, it will fetch ALL the repositories on GitHub. /// /// API docs: https://developer.github.com/v3/repos/#list-all-public-repositories - Stream listPublicRepositories({int limit = 50, DateTime since}) { + Stream listPublicRepositories({int limit = 50, DateTime? since}) { final params = {}; if (since != null) { params['since'] = since.toIso8601String(); } - final pages = limit != null ? (limit / 30).ceil() : null; + final pages = (limit / 30).ceil(); return PaginationHelper(github) .fetchStreamed('GET', '/repositories', pages: pages, params: params) .expand((http.Response response) { final list = jsonDecode(response.body) as List>; - return list.map((Map it) => Repository.fromJson(it)); + return list.map(Repository.fromJson); }); } @@ -105,19 +101,19 @@ class RepositoriesService extends Service { /// /// API docs: https://developer.github.com/v3/repos/#create Future createRepository(CreateRepository repository, - {String org}) async { + {String? org}) async { ArgumentError.checkNotNull(repository); if (org != null) { - return github.postJSON, TeamRepository>( + return github.postJSON, Repository>( '/orgs/$org/repos', body: GitHubJson.encode(repository), - convert: (i) => TeamRepository.fromJson(i), + convert: Repository.fromJson, ); } else { return github.postJSON, Repository>( '/user/repos', body: GitHubJson.encode(repository), - convert: (i) => Repository.fromJson(i), + convert: Repository.fromJson, ); } } @@ -126,7 +122,7 @@ class RepositoriesService extends Service { ArgumentError.checkNotNull(slug); return github.getJSON, LicenseDetails>( '/repos/${slug.owner}/${slug.name}/license', - convert: (json) => LicenseDetails.fromJson(json), + convert: LicenseDetails.fromJson, ); } @@ -137,7 +133,7 @@ class RepositoriesService extends Service { ArgumentError.checkNotNull(slug); return github.getJSON, Repository>( '/repos/${slug.owner}/${slug.name}', - convert: (i) => Repository.fromJson(i), + convert: Repository.fromJson, statusCode: StatusCodes.OK, fail: (http.Response response) { if (response.statusCode == 404) { @@ -159,22 +155,22 @@ class RepositoriesService extends Service { /// /// API docs: https://developer.github.com/v3/repos/#edit Future editRepository(RepositorySlug slug, - {String name, - String description, - String homepage, - bool private, - bool hasIssues, - bool hasWiki, - bool hasDownloads}) async { + {String? name, + String? description, + String? homepage, + bool? private, + bool? hasIssues, + bool? hasWiki, + bool? hasDownloads}) async { ArgumentError.checkNotNull(slug); final data = createNonNullMap({ - 'name': name, - 'description': description, - 'homepage': homepage, - 'private': private, - 'has_issues': hasIssues, - 'has_wiki': hasWiki, - 'has_downloads': hasDownloads, + 'name': name!, + 'description': description!, + 'homepage': homepage!, + 'private': private!, + 'has_issues': hasIssues!, + 'has_wiki': hasWiki!, + 'has_downloads': hasDownloads!, 'default_branch': 'defaultBranch' }); return github.postJSON( @@ -210,7 +206,7 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, Contributor>( 'GET', '/repos/${slug.fullName}/contributors', - (i) => Contributor.fromJson(i), + Contributor.fromJson, params: {'anon': anon.toString()}, ); } @@ -223,7 +219,7 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, Team>( 'GET', '/repos/${slug.fullName}/teams', - (i) => Team.fromJson(i), + Team.fromJson, ); } @@ -242,13 +238,12 @@ class RepositoriesService extends Service { /// Lists the tags of the specified repository. /// /// API docs: https://developer.github.com/v3/repos/#list-tags - Stream listTags(RepositorySlug slug) { + Stream listTags(RepositorySlug slug, + {int page = 1, int? pages, int perPage = 30}) { ArgumentError.checkNotNull(slug); return PaginationHelper(github).objects, Tag>( - 'GET', - '/repos/${slug.fullName}/tags', - (i) => Tag.fromJson(i), - ); + 'GET', '/repos/${slug.fullName}/tags', Tag.fromJson, + pages: pages, params: {'page': page, 'per_page': perPage}); } /// Lists the branches of the specified repository. @@ -259,7 +254,7 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, Branch>( 'GET', '/repos/${slug.fullName}/branches', - (i) => Branch.fromJson(i), + Branch.fromJson, ); } @@ -271,7 +266,7 @@ class RepositoriesService extends Service { ArgumentError.checkNotNull(branch); return github.getJSON, Branch>( '/repos/${slug.fullName}/branches/$branch', - convert: (i) => Branch.fromJson(i), + convert: Branch.fromJson, ); } @@ -283,7 +278,7 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, Collaborator>( 'GET', '/repos/${slug.fullName}/collaborators', - (json) => Collaborator.fromJson(json), + Collaborator.fromJson, ); } @@ -351,7 +346,7 @@ class RepositoriesService extends Service { .objects, CommitComment>( 'GET', '/repos/${slug.fullName}/commits/${commit.sha}/comments', - (i) => CommitComment.fromJson(i), + CommitComment.fromJson, statusCode: StatusCodes.OK, ); } @@ -365,7 +360,7 @@ class RepositoriesService extends Service { .objects, CommitComment>( 'GET', 'repos/${slug.fullName}/comments', - (i) => CommitComment.fromJson(i), + CommitComment.fromJson, statusCode: StatusCodes.OK, ); } @@ -380,24 +375,24 @@ class RepositoriesService extends Service { Future createCommitComment( RepositorySlug slug, RepositoryCommit commit, { - @required String body, - String path, - int position, - @Deprecated('Use position parameter instead') int line, + required String body, + String? path, + int? position, + @Deprecated('Use position parameter instead') int? line, }) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(commit); final data = createNonNullMap({ 'body': body, - 'path': path, - 'position': position, - 'line': line, + 'path': path!, + 'position': position!, + 'line': line!, }); return github.postJSON, CommitComment>( '/repos/${slug.fullName}/commits/${commit.sha}/comments', body: GitHubJson.encode(data), statusCode: StatusCodes.CREATED, - convert: (i) => CommitComment.fromJson(i), + convert: CommitComment.fromJson, ); } @@ -405,13 +400,13 @@ class RepositoriesService extends Service { /// /// https://developer.github.com/v3/repos/comments/#get-a-single-commit-comment Future getCommitComment(RepositorySlug slug, - {@required int id}) async { + {required int id}) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(id); return github.getJSON, CommitComment>( '/repos/${slug.fullName}/comments/$id', statusCode: StatusCodes.OK, - convert: (i) => CommitComment.fromJson(i), + convert: CommitComment.fromJson, ); } @@ -423,7 +418,7 @@ class RepositoriesService extends Service { /// /// https://developer.github.com/v3/repos/comments/#update-a-commit-comment Future updateCommitComment(RepositorySlug slug, - {@required int id, @required String body}) async { + {required int id, required String body}) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(id); ArgumentError.checkNotNull(body); @@ -431,7 +426,7 @@ class RepositoriesService extends Service { '/repos/${slug.fullName}/comments/$id', body: GitHubJson.encode(createNonNullMap({'body': body})), statusCode: StatusCodes.OK, - convert: (i) => CommitComment.fromJson(i), + convert: CommitComment.fromJson, ); } @@ -440,7 +435,7 @@ class RepositoriesService extends Service { /// /// https://developer.github.com/v3/repos/comments/#delete-a-commit-comment Future deleteCommitComment(RepositorySlug slug, - {@required int id}) async { + {required int id}) async { ArgumentError.checkNotNull(slug); return github .request( @@ -453,14 +448,41 @@ class RepositoriesService extends Service { /// Lists the commits of the provided repository [slug]. /// + /// [sha] is the SHA or branch to start listing commits from. Default: the + /// repository’s default branch (usually main). + /// + /// [path] will only show commits that changed that file path. + /// + /// [author] and [committer] are the GitHub username to filter commits for. + /// + /// [since] shows commit after this time, and [until] shows commits before + /// this time. + /// /// API docs: https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository - Stream listCommits(RepositorySlug slug) { + Stream listCommits( + RepositorySlug slug, { + String? sha, + String? path, + String? author, + String? committer, + DateTime? since, + DateTime? until, + }) { ArgumentError.checkNotNull(slug); + final params = { + if (author != null) 'author': author, + if (committer != null) 'committer': committer, + if (sha != null) 'sha': sha, + if (path != null) 'path': path, + if (since != null) 'since': since.toIso8601String(), + if (until != null) 'until': until.toIso8601String(), + }; return PaginationHelper(github) .objects, RepositoryCommit>( 'GET', '/repos/${slug.fullName}/commits', - (i) => RepositoryCommit.fromJson(i), + RepositoryCommit.fromJson, + params: params, ); } @@ -472,7 +494,7 @@ class RepositoriesService extends Service { ArgumentError.checkNotNull(sha); return github.getJSON, RepositoryCommit>( '/repos/${slug.fullName}/commits/$sha', - convert: (i) => RepositoryCommit.fromJson(i), + convert: RepositoryCommit.fromJson, statusCode: StatusCodes.OK, ); } @@ -506,7 +528,7 @@ class RepositoriesService extends Service { ArgumentError.checkNotNull(refHead); return github.getJSON, GitHubComparison>( '/repos/${slug.fullName}/compare/$refBase...$refHead', - convert: (j) => GitHubComparison.fromJson(j), + convert: GitHubComparison.fromJson, ); } @@ -516,7 +538,7 @@ class RepositoriesService extends Service { /// is defined, the repository's default branch is used (usually master). /// /// API docs: https://developer.github.com/v3/repos/contents/#get-the-readme - Future getReadme(RepositorySlug slug, {String ref}) async { + Future getReadme(RepositorySlug slug, {String? ref}) async { ArgumentError.checkNotNull(slug); final headers = {}; @@ -533,9 +555,7 @@ class RepositoriesService extends Service { } }, convert: (Map input) { var file = GitHubFile.fromJson(input); - if (file != null && slug != null) { - file.sourceRepository = slug; - } + file.sourceRepository = slug; return file; }); } @@ -556,7 +576,7 @@ class RepositoriesService extends Service { /// /// API docs: https://developer.github.com/v3/repos/contents/#get-contents Future getContents(RepositorySlug slug, String path, - {String ref}) async { + {String? ref}) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(path); var url = '/repos/${slug.fullName}/contents/$path'; @@ -567,7 +587,7 @@ class RepositoriesService extends Service { return github.getJSON( url, - convert: (input) { + convert: (dynamic input) { final contents = RepositoryContents(); if (input is Map) { // Weird one-off. If the content of `input` is JSON w/ a message @@ -579,8 +599,10 @@ class RepositoriesService extends Service { } contents.file = GitHubFile.fromJson(input as Map); } else { - contents.tree = - (input as List).map((it) => GitHubFile.fromJson(it)).toList(); + contents.tree = (input as List) + .cast>() + .map(GitHubFile.fromJson) + .toList(); } return contents; }, @@ -608,14 +630,14 @@ class RepositoriesService extends Service { /// API docs: https://developer.github.com/v3/repos/contents/#update-a-file Future updateFile(RepositorySlug slug, String path, String message, String content, String sha, - {String branch}) async { + {String? branch}) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(path); final map = createNonNullMap({ 'message': message, 'content': content, 'sha': sha, - 'branch': branch, + 'branch': branch!, }); final response = await github.request( 'PUT', @@ -648,7 +670,7 @@ class RepositoriesService extends Service { /// Gets an archive link for the specified repository and reference. /// /// API docs: https://developer.github.com/v3/repos/contents/#get-archive-link - Future getArchiveLink(RepositorySlug slug, String ref, + Future getArchiveLink(RepositorySlug slug, String ref, {String format = 'tarball'}) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(ref); @@ -669,20 +691,20 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, Repository>( 'GET', '/repos/${slug.fullName}/forks', - (i) => Repository.fromJson(i), + Repository.fromJson, ); } /// Creates a fork for the authenticated user. /// /// API docs: https://developer.github.com/v3/repos/forks/#create-a-fork - Future createFork(RepositorySlug slug, [CreateFork fork]) async { + Future createFork(RepositorySlug slug, [CreateFork? fork]) async { ArgumentError.checkNotNull(slug); fork ??= CreateFork(); return github.postJSON, Repository>( '/repos/${slug.fullName}/forks', body: GitHubJson.encode(fork), - convert: (i) => Repository.fromJson(i), + convert: Repository.fromJson, ); } @@ -740,18 +762,18 @@ class RepositoriesService extends Service { Future editHook( RepositorySlug slug, Hook hookToEdit, { - String configUrl, - String configContentType, - String configSecret, - bool configInsecureSsl, - List events, - List addEvents, - List removeEvents, - bool active, + String? configUrl, + String? configContentType, + String? configSecret, + bool? configInsecureSsl, + List? events, + List? addEvents, + List? removeEvents, + bool? active, }) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(hookToEdit); - ArgumentError.checkNotNull(configUrl ?? hookToEdit.config.url); + ArgumentError.checkNotNull(configUrl ?? hookToEdit.config!.url); if (configContentType != 'json' && configContentType != 'form') { throw ArgumentError.value(configContentType, 'configContentType'); } @@ -765,9 +787,9 @@ class RepositoriesService extends Service { 'add_events': addEvents, 'remove_events': removeEvents, 'config': { - 'url': configUrl ?? hookToEdit.config.url, - 'content_type': configContentType ?? hookToEdit.config.contentType, - 'secret': configSecret ?? hookToEdit.config.secret, + 'url': configUrl ?? hookToEdit.config!.url, + 'content_type': configContentType ?? hookToEdit.config!.contentType, + 'secret': configSecret ?? hookToEdit.config!.secret, 'insecure_ssl': configInsecureSsl == null || !configInsecureSsl ? '0' : '1', }, @@ -827,7 +849,7 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, PublicKey>( 'GET', '/repos/${slug.fullName}/keys', - (i) => PublicKey.fromJson(i), + PublicKey.fromJson, ); } @@ -835,14 +857,13 @@ class RepositoriesService extends Service { /// * [id]: id of the key to retrieve. /// /// https://developer.github.com/v3/repos/keys/#get - Future getDeployKey(RepositorySlug slug, - {@required int id}) async { + Future getDeployKey(RepositorySlug slug, {required int id}) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(id); return github.getJSON, PublicKey>( '/repos/${slug.fullName}/keys/$id', statusCode: StatusCodes.OK, - convert: (i) => PublicKey.fromJson(i), + convert: PublicKey.fromJson, ); } @@ -857,7 +878,7 @@ class RepositoriesService extends Service { '/repos/${slug.fullName}/keys', body: GitHubJson.encode(key), statusCode: StatusCodes.CREATED, - convert: (i) => PublicKey.fromJson(i), + convert: PublicKey.fromJson, ); } @@ -865,7 +886,7 @@ class RepositoriesService extends Service { /// /// https://developer.github.com/v3/repos/keys/#delete Future deleteDeployKey( - {@required RepositorySlug slug, @required PublicKey key}) async { + {required RepositorySlug slug, required PublicKey key}) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(key); return github @@ -886,7 +907,7 @@ class RepositoriesService extends Service { return github.postJSON, RepositoryCommit>( '/repos/${slug.fullName}/merges', body: GitHubJson.encode(merge), - convert: (i) => RepositoryCommit.fromJson(i), + convert: RepositoryCommit.fromJson, statusCode: StatusCodes.CREATED, ); } @@ -899,7 +920,7 @@ class RepositoriesService extends Service { return github.getJSON, RepositoryPages>( '/repos/${slug.fullName}/pages', statusCode: StatusCodes.OK, - convert: (i) => RepositoryPages.fromJson(i), + convert: RepositoryPages.fromJson, ); } @@ -911,7 +932,7 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, PageBuild>( 'GET', '/repos/${slug.fullName}/pages/builds', - (i) => PageBuild.fromJson(i), + PageBuild.fromJson, statusCode: StatusCodes.OK, ); } @@ -923,7 +944,7 @@ class RepositoriesService extends Service { ArgumentError.checkNotNull(slug); return github.getJSON( '/repos/${slug.fullName}/pages/builds/latest', - convert: (i) => PageBuild.fromJson(i), + convert: PageBuild.fromJson, statusCode: StatusCodes.OK, ); } @@ -938,7 +959,7 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, Release>( 'GET', '/repos/${slug.fullName}/releases', - (i) => Release.fromJson(i), + Release.fromJson, ); } @@ -949,7 +970,7 @@ class RepositoriesService extends Service { ArgumentError.checkNotNull(slug); return github.getJSON, Release>( '/repos/${slug.fullName}/releases/latest', - convert: (i) => Release.fromJson(i), + convert: Release.fromJson, statusCode: StatusCodes.OK, ); } @@ -962,7 +983,7 @@ class RepositoriesService extends Service { ArgumentError.checkNotNull(id); return github.getJSON, Release>( '/repos/${slug.fullName}/releases/$id', - convert: (i) => Release.fromJson(i), + convert: Release.fromJson, ); } @@ -973,10 +994,10 @@ class RepositoriesService extends Service { /// /// API docs: https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name Future getReleaseByTagName( - RepositorySlug slug, String tagName) async { + RepositorySlug slug, String? tagName) async { return github.getJSON( '/repos/${slug.fullName}/releases/tags/$tagName', - convert: (i) => Release.fromJson(i), + convert: Release.fromJson, statusCode: StatusCodes.OK, fail: (http.Response response) { if (response.statusCode == 404) { @@ -1000,18 +1021,23 @@ class RepositoriesService extends Service { ArgumentError.checkNotNull(createRelease); final release = await github.postJSON, Release>( '/repos/${slug.fullName}/releases', - convert: (i) => Release.fromJson(i), + convert: Release.fromJson, body: GitHubJson.encode(createRelease.toJson()), statusCode: StatusCodes.CREATED); if (release.hasErrors) { - final alreadyExistsErrorCode = release.errors.firstWhere( + final alreadyExistsErrorCode = release.errors!.firstWhere( (error) => error['code'] == 'already_exists', orElse: () => null, ); if (alreadyExistsErrorCode != null) { final field = alreadyExistsErrorCode['field']; if (field == 'tag_name') { - return getReleaseByTagName(slug, createRelease.tagName); + if (getIfExists) { + return getReleaseByTagName(slug, createRelease.tagName); + } else { + throw Exception( + 'Tag / Release already exists ${createRelease.tagName}'); + } } } else { print( @@ -1035,12 +1061,12 @@ class RepositoriesService extends Service { Future editRelease( RepositorySlug slug, Release releaseToEdit, { - String tagName, - String targetCommitish, - String name, - String body, - bool draft, - bool preRelease, + String? tagName, + String? targetCommitish, + String? name, + String? body, + bool? draft, + bool? preRelease, }) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(releaseToEdit); @@ -1055,7 +1081,7 @@ class RepositoriesService extends Service { 'prerelease': preRelease ?? releaseToEdit.isPrerelease, })), statusCode: StatusCodes.OK, - convert: (i) => Release.fromJson(i), + convert: Release.fromJson, ); } @@ -1083,7 +1109,7 @@ class RepositoriesService extends Service { return PaginationHelper(github).objects, ReleaseAsset>( 'GET', '/repos/${slug.fullName}/releases/${release.id}/assets', - (i) => ReleaseAsset.fromJson(i), + ReleaseAsset.fromJson, statusCode: StatusCodes.OK, ); } @@ -1093,13 +1119,13 @@ class RepositoriesService extends Service { /// API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release-asset // TODO: implement a way to retrieve the asset's binary content Future getReleaseAsset(RepositorySlug slug, Release release, - {@required int assetId}) async { + {required int assetId}) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(release); return github.postJSON, ReleaseAsset>( '/repos/${slug.fullName}/releases/assets/$assetId', statusCode: StatusCodes.OK, - convert: (i) => ReleaseAsset.fromJson(i), + convert: ReleaseAsset.fromJson, ); } @@ -1109,15 +1135,15 @@ class RepositoriesService extends Service { Future editReleaseAsset( RepositorySlug slug, ReleaseAsset assetToEdit, { - String name, - String label, + String? name, + String? label, }) async { ArgumentError.checkNotNull(slug); ArgumentError.checkNotNull(assetToEdit); return github.postJSON, ReleaseAsset>( '/repos/${slug.fullName}/releases/assets/${assetToEdit.id}', statusCode: StatusCodes.OK, - convert: (i) => ReleaseAsset.fromJson(i), + convert: ReleaseAsset.fromJson, body: GitHubJson.encode(createNonNullMap({ 'name': name ?? assetToEdit.name, 'label': label ?? assetToEdit.label, @@ -1155,7 +1181,7 @@ class RepositoriesService extends Service { ), headers: headers, body: createReleaseAsset.assetData, - convert: (i) => ReleaseAsset.fromJson(i)); + convert: ReleaseAsset.fromJson); releaseAssets.add(releaseAsset); } return releaseAssets; @@ -1177,7 +1203,8 @@ class RepositoriesService extends Service { if (response.statusCode == StatusCodes.OK) { return (jsonDecode(response.body) as List) - .map((e) => ContributorStatistics.fromJson(e)) + .cast>() + .map(ContributorStatistics.fromJson) .toList(); } else if (response.statusCode == StatusCodes.ACCEPTED) { throw NotReady(github, path); @@ -1194,7 +1221,7 @@ class RepositoriesService extends Service { .objects, YearCommitCountWeek>( 'GET', '/repos/${slug.fullName}/stats/commit_activity', - (i) => YearCommitCountWeek.fromJson(i), + YearCommitCountWeek.fromJson, ); } @@ -1207,7 +1234,7 @@ class RepositoriesService extends Service { .objects, WeeklyChangesCount>( 'GET', '/repos/${slug.fullName}/stats/code_frequency', - (i) => WeeklyChangesCount.fromJson(i), + WeeklyChangesCount.fromJson, ); } @@ -1219,7 +1246,7 @@ class RepositoriesService extends Service { return github.getJSON( '/repos/${slug.fullName}/stats/participation', statusCode: StatusCodes.OK, - convert: (i) => ContributorParticipation.fromJson(i), + convert: ContributorParticipation.fromJson, ); } @@ -1232,7 +1259,7 @@ class RepositoriesService extends Service { .objects, PunchcardEntry>( 'GET', '/repos/${slug.fullName}/stats/punchcard', - (i) => PunchcardEntry.fromJson(i), + PunchcardEntry.fromJson, ); } @@ -1247,7 +1274,7 @@ class RepositoriesService extends Service { .objects, RepositoryStatus>( 'GET', '/repos/${slug.fullName}/commits/$ref/statuses', - (i) => RepositoryStatus.fromJson(i), + RepositoryStatus.fromJson, ); } @@ -1263,7 +1290,7 @@ class RepositoriesService extends Service { return github.postJSON, RepositoryStatus>( '/repos/${slug.fullName}/statuses/$ref', body: GitHubJson.encode(request), - convert: (i) => RepositoryStatus.fromJson(i), + convert: RepositoryStatus.fromJson, ); } @@ -1276,8 +1303,24 @@ class RepositoriesService extends Service { ArgumentError.checkNotNull(ref); return github.getJSON, CombinedRepositoryStatus>( '/repos/${slug.fullName}/commits/$ref/status', - convert: (i) => CombinedRepositoryStatus.fromJson(i), + convert: CombinedRepositoryStatus.fromJson, + statusCode: StatusCodes.OK, + ); + } + + /// Generate a name and body describing a release. The body content will be + /// markdown formatted and contain information like the changes since last + /// release and users who contributed. The generated release notes are not + /// saved anywhere. They are intended to be generated and used when + /// creating a new release. + /// + /// API docs: https://docs.github.com/en/rest/reference/repos#generate-release-notes-content-for-a-release + Future generateReleaseNotes(CreateReleaseNotes crn) async { + return github.postJSON, ReleaseNotes>( + '/repos/${crn.owner}/${crn.repo}/releases/generate-notes', + body: GitHubJson.encode(crn), statusCode: StatusCodes.OK, + convert: ReleaseNotes.fromJson, ); } } diff --git a/lib/src/common/search_service.dart b/lib/src/common/search_service.dart index 4418b6c6..27cf6eef 100644 --- a/lib/src/common/search_service.dart +++ b/lib/src/common/search_service.dart @@ -1,21 +1,20 @@ import 'dart:async'; import 'dart:convert'; + import 'package:github/src/common.dart'; -import 'package:github/src/common/model/users.dart'; -import 'package:github/src/common/util/pagination.dart'; /// The [SearchService] handles communication with search related methods of /// the GitHub API. /// /// API docs: https://developer.github.com/v3/search/ class SearchService extends Service { - SearchService(GitHub github) : super(github); + SearchService(super.github); /// Search for repositories using [query]. /// Since the Search Rate Limit is small, this is a best effort implementation. /// /// API docs: https://developer.github.com/v3/search/#search-repositories - Stream repositories(String query, {String sort, int pages = 2}) { + Stream repositories(String query, {String? sort, int pages = 2}) { final params = {'q': query}; if (sort != null) { params['sort'] = sort; @@ -45,7 +44,10 @@ class SearchService extends Service { final items = input['items'] as List; - items.map((item) => Repository.fromJson(item)).forEach(controller.add); + items + .cast>() + .map(Repository.fromJson) + .forEach(controller.add); }).onDone(controller.close); return controller.stream; @@ -65,17 +67,17 @@ class SearchService extends Service { /// https://developer.github.com/v3/search/#search-code Stream code( String query, { - int pages, - int perPage, - String language, - String filename, - String extension, - String user, - String org, - String repo, - String fork, - String path, - String size, + int? pages, + int? perPage, + String? language, + String? filename, + String? extension, + String? user, + String? org, + String? repo, + String? fork, + String? path, + String? size, bool inFile = true, bool inPath = false, }) { @@ -95,23 +97,23 @@ class SearchService extends Service { query += _searchQualifier('size', size); // build up the in: qualifier based on the 2 booleans - var _in = ''; + var inValue = ''; if (inFile) { - _in = 'file'; + inValue = 'file'; } if (inPath) { - if (_in.isEmpty) { - _in = 'path'; + if (inValue.isEmpty) { + inValue = 'path'; } else { - _in = 'file,path'; + inValue = 'file,path'; } } - if (_in.isNotEmpty) { - query += ' in:$_in'; + if (inValue.isNotEmpty) { + query += ' in:$inValue'; } final params = {}; - params['q'] = query ?? ''; + params['q'] = query; if (perPage != null) { params['per_page'] = perPage.toString(); } @@ -121,7 +123,7 @@ class SearchService extends Service { .map((r) => CodeSearchResults.fromJson(json.decode(r.body))); } - String _searchQualifier(String key, String value) { + String _searchQualifier(String key, String? value) { if (value != null && value.isNotEmpty) { return ' $key:$value'; } @@ -131,7 +133,7 @@ class SearchService extends Service { /// Search for issues and pull-requests using [query]. /// Since the Search Rate Limit is small, this is a best effort implementation. /// API docs: https://developer.github.com/v3/search/#search-issues - Stream issues(String query, {String sort, int pages = 2}) { + Stream issues(String query, {String? sort, int pages = 2}) { final params = {'q': query}; if (sort != null) { params['sort'] = sort; @@ -160,7 +162,10 @@ class SearchService extends Service { final items = input['items'] as List; - items.map((item) => Issue.fromJson(item)).forEach(controller.add); + items + .cast>() + .map(Issue.fromJson) + .forEach(controller.add); }).onDone(controller.close); return controller.stream; @@ -172,7 +177,7 @@ class SearchService extends Service { /// API docs: https://developer.github.com/v3/search/#search-users Stream users( String query, { - String sort, + String? sort, int pages = 2, int perPage = 30, }) { @@ -207,7 +212,10 @@ class SearchService extends Service { final items = input['items'] as List; - items.map((item) => User.fromJson(item)).forEach(controller.add); + items + .cast>() + .map(User.fromJson) + .forEach(controller.add); }).onDone(controller.close); return controller.stream; diff --git a/lib/src/common/url_shortener_service.dart b/lib/src/common/url_shortener_service.dart index 950b0844..60db2958 100644 --- a/lib/src/common/url_shortener_service.dart +++ b/lib/src/common/url_shortener_service.dart @@ -6,11 +6,11 @@ import 'package:github/src/common.dart'; /// /// API docs: https://github.com/blog/985-git-io-github-url-shortener class UrlShortenerService extends Service { - UrlShortenerService(GitHub github) : super(github); + UrlShortenerService(super.github); /// Shortens the provided [url]. An optional [code] can be provided to create /// your own vanity URL. - Future shortenUrl(String url, {String code}) { + Future shortenUrl(String url, {String? code}) { final params = {}; params['url'] = url; @@ -26,7 +26,7 @@ class UrlShortenerService extends Service { throw GitHubError(github, 'Failed to create shortened url!'); } - return response.headers['Location'].split('/').last; + return response.headers['Location']!.split('/').last; }); } } diff --git a/lib/src/common/users_service.dart b/lib/src/common/users_service.dart index e6e3b829..6485f4b0 100644 --- a/lib/src/common/users_service.dart +++ b/lib/src/common/users_service.dart @@ -1,8 +1,6 @@ import 'dart:async'; + import 'package:github/src/common.dart'; -import 'package:github/src/common/model/users.dart'; -import 'package:github/src/common/util/pagination.dart'; -import 'package:github/src/common/util/utils.dart'; import 'package:http/http.dart' as http; /// The [UsersService] handles communication with user related methods of the @@ -10,25 +8,25 @@ import 'package:http/http.dart' as http; /// /// API docs: https://developer.github.com/v3/users/ class UsersService extends Service { - UsersService(GitHub github) : super(github); + UsersService(super.github); /// Fetches the user specified by [name]. /// /// API docs: https://developer.github.com/v3/users/#get-a-single-user - Future getUser(String name) => - github.getJSON('/users/$name', convert: (i) => User.fromJson(i)); + Future getUser(String? name) => + github.getJSON('/users/$name', convert: User.fromJson); /// Updates the Current User. /// /// API docs: https://developer.github.com/v3/users/#update-the-authenticated-user Future editCurrentUser( - {String name, - String email, - String blog, - String company, - String location, - bool hireable, - String bio}) { + {String? name, + String? email, + String? blog, + String? company, + String? location, + bool? hireable, + String? bio}) { final map = createNonNullMap({ 'name': name, 'email': email, @@ -43,12 +41,12 @@ class UsersService extends Service { '/user', body: GitHubJson.encode(map), statusCode: 200, - convert: (i) => CurrentUser.fromJson(i), + convert: CurrentUser.fromJson, ); } /// Fetches a list of users specified by [names]. - Stream getUsers(List names, {int pages}) async* { + Stream getUsers(List names, {int? pages}) async* { for (final name in names) { final user = await getUser(name); yield user; @@ -60,14 +58,13 @@ class UsersService extends Service { /// Throws [AccessForbidden] if we are not authenticated. /// /// API docs: https://developer.github.com/v3/users/#get-the-authenticated-user - Future getCurrentUser() => github.getJSON('/user', - statusCode: StatusCodes.OK, - fail: (http.Response response) { + Future getCurrentUser() => + github.getJSON('/user', statusCode: StatusCodes.OK, + fail: (http.Response response) { if (response.statusCode == StatusCodes.FORBIDDEN) { throw AccessForbidden(github); } - }, - convert: (i) => CurrentUser.fromJson(i)); + }, convert: CurrentUser.fromJson); /// Checks if a user exists. Future isUser(String name) => github @@ -79,21 +76,21 @@ class UsersService extends Service { /// Lists all users. /// /// API docs: https://developer.github.com/v3/users/#get-all-users - Stream listUsers({int pages, int since}) => - PaginationHelper(github).objects('GET', '/users', (i) => User.fromJson(i), + Stream listUsers({int? pages, int? since}) => + PaginationHelper(github).objects('GET', '/users', User.fromJson, pages: pages, params: {'since': since}); /// Lists all email addresses for the currently authenticated user. /// /// API docs: https://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user Stream listEmails() => PaginationHelper(github) - .objects('GET', '/user/emails', (i) => UserEmail.fromJson(i)); + .objects('GET', '/user/emails', UserEmail.fromJson); /// Add Emails /// /// API docs: https://developer.github.com/v3/users/emails/#add-email-addresses Stream addEmails(List emails) => PaginationHelper(github) - .objects('POST', '/user/emails', (i) => UserEmail.fromJson(i), + .objects('POST', '/user/emails', UserEmail.fromJson, statusCode: 201, body: GitHubJson.encode(emails)); /// Delete Emails @@ -108,8 +105,7 @@ class UsersService extends Service { /// /// API docs: https://developer.github.com/v3/users/followers/#list-followers-of-a-user Stream listUserFollowers(String user) => PaginationHelper(github) - .objects('GET', '/users/$user/followers', (i) => User.fromJson(i), - statusCode: 200); + .objects('GET', '/users/$user/followers', User.fromJson, statusCode: 200); /// Check if the current user is following the specified user. Future isFollowingUser(String user) => @@ -146,26 +142,23 @@ class UsersService extends Service { /// List current user followers. /// /// API docs: https://developer.github.com/v3/users/followers/#list-followers-of-a-user - Stream listCurrentUserFollowers() => PaginationHelper(github).objects( - 'GET', '/user/followers', (i) => User.fromJson(i), - statusCode: 200); + Stream listCurrentUserFollowers() => PaginationHelper(github) + .objects('GET', '/user/followers', User.fromJson, statusCode: 200); /// List current user following /// /// API docs: https://developer.github.com/v3/users/followers/#list-users-followed-by-the-authenticated-user - Stream listCurrentUserFollowing() => PaginationHelper(github).objects( - 'GET', '/user/following', (i) => User.fromJson(i), - statusCode: 200); + Stream listCurrentUserFollowing() => PaginationHelper(github) + .objects('GET', '/user/following', User.fromJson, statusCode: 200); /// Lists the verified public keys for a [userLogin]. If no [userLogin] is specified, /// the public keys for the authenticated user are fetched. /// /// API docs: https://developer.github.com/v3/users/keys/#list-public-keys-for-a-user /// and https://developer.github.com/v3/users/keys/#list-your-public-keys - Stream listPublicKeys([String userLogin]) { + Stream listPublicKeys([String? userLogin]) { final path = userLogin == null ? '/user/keys' : '/users/$userLogin/keys'; - return PaginationHelper(github) - .objects('GET', path, (i) => PublicKey.fromJson(i)); + return PaginationHelper(github).objects('GET', path, PublicKey.fromJson); } // TODO: Implement getPublicKey: https://developer.github.com/v3/users/keys/#get-a-single-public-key diff --git a/lib/src/common/util/auth.dart b/lib/src/common/util/auth.dart index 9a11ea24..1c4d6798 100644 --- a/lib/src/common/util/auth.dart +++ b/lib/src/common/util/auth.dart @@ -1,34 +1,75 @@ +import 'dart:convert'; + /// Authentication information. class Authentication { /// OAuth2 Token - final String token; + final String? token; /// GitHub Username - final String username; + final String? username; /// GitHub Password - final String password; + final String? password; + + final String? bearerToken; + // TODO: mark the pram as `String` to REQUIRE a non-null value. + // NEXT major version /// Creates an [Authentication] instance that uses the specified OAuth2 [token]. - Authentication.withToken(this.token) + const Authentication.withToken(this.token) : username = null, - password = null; + password = null, + bearerToken = null; + + /// Creates an [Authentication] instance that uses the specified + /// [bearerToken]. + const Authentication.bearerToken(String this.bearerToken) + : username = null, + password = null, + token = null; /// Creates an [Authentication] instance that has no authentication. - Authentication.anonymous() + const Authentication.anonymous() : token = null, username = null, - password = null; + password = null, + bearerToken = null; + // TODO: mark the `username` and `password` params as `String` to REQUIRE + // non-null values. - NEXT major version /// Creates an [Authentication] instance that uses a username and password. - Authentication.basic(this.username, this.password) : token = null; + const Authentication.basic(this.username, this.password) + : token = null, + bearerToken = null; /// Anonymous Authentication Flag - bool get isAnonymous => !isBasic && !isToken; + bool get isAnonymous => !isBasic && !isToken && !isBearer; /// Basic Authentication Flag bool get isBasic => username != null; /// Token Authentication Flag bool get isToken => token != null; + + // This instance represents a authentication with a "Bearer" token. + bool get isBearer => bearerToken != null; + + /// Returns a value for the `Authorization` HTTP request header or `null` + /// if [isAnonymous] is `true`. + String? authorizationHeaderValue() { + if (isToken) { + return 'token $token'; + } + + if (isBasic) { + final userAndPass = base64Encode(utf8.encode('$username:$password')); + return 'basic $userAndPass'; + } + + if (isBearer) { + return 'Bearer $bearerToken'; + } + + return null; + } } diff --git a/lib/src/common/util/crawler.dart b/lib/src/common/util/crawler.dart index 62018924..5b5a7f7e 100644 --- a/lib/src/common/util/crawler.dart +++ b/lib/src/common/util/crawler.dart @@ -12,9 +12,9 @@ class RepositoryCrawler { Stream scan(String path) async* { final contents = await github.repositories.getContents(slug, path); - for (final content in contents.tree) { + for (final content in contents.tree!) { if (content.type == 'dir') { - yield* scan(content.path); + yield* scan(content.path!); } else { yield content; } diff --git a/lib/src/common/util/errors.dart b/lib/src/common/util/errors.dart index a140f583..14625a6c 100644 --- a/lib/src/common/util/errors.dart +++ b/lib/src/common/util/errors.dart @@ -2,10 +2,10 @@ import 'package:github/src/common.dart'; /// Error Generated by [GitHub] class GitHubError implements Exception { - final String message; - final String apiUrl; + final String? message; + final String? apiUrl; final GitHub github; - final Object source; + final Object? source; const GitHubError(this.github, this.message, {this.apiUrl, this.source}); @@ -25,14 +25,13 @@ class NotReady extends GitHubError { /// GitHub Entity was not found class NotFound extends GitHubError { const NotFound( - GitHub github, - String msg, - ) : super(github, msg); + super.github, + String super.msg, + ); } class BadRequest extends GitHubError { - const BadRequest(GitHub github, [String msg = 'Not Found']) - : super(github, msg); + const BadRequest(super.github, [super.msg = 'Not Found']); } /// GitHub Repository was not found @@ -43,7 +42,7 @@ class RepositoryNotFound extends NotFound { /// Release not found class ReleaseNotFound extends NotFound { - const ReleaseNotFound.fromTagName(GitHub github, String tagName) + const ReleaseNotFound.fromTagName(GitHub github, String? tagName) : super(github, 'Release for tagName $tagName Not Found.'); } @@ -55,7 +54,7 @@ class UserNotFound extends NotFound { /// GitHub Organization was not found class OrganizationNotFound extends NotFound { - const OrganizationNotFound(GitHub github, String organization) + const OrganizationNotFound(GitHub github, String? organization) : super(github, 'Organization Not Found: $organization'); } @@ -77,13 +76,13 @@ class RateLimitHit extends GitHubError { /// A GitHub Server Error class ServerError extends GitHubError { - ServerError(GitHub github, int statusCode, String message) + ServerError(GitHub github, int statusCode, String? message) : super(github, '${message ?? 'Server Error'} ($statusCode)'); } /// An Unknown Error class UnknownError extends GitHubError { - const UnknownError(GitHub github, [String message]) + const UnknownError(GitHub github, [String? message]) : super(github, message ?? 'Unknown Error'); } @@ -94,11 +93,10 @@ class NotAuthenticated extends GitHubError { } class InvalidJSON extends BadRequest { - const InvalidJSON(GitHub github, [String message = 'Invalid JSON']) - : super(github, message); + const InvalidJSON(super.github, [super.message = 'Invalid JSON']); } class ValidationFailed extends GitHubError { - const ValidationFailed(GitHub github, [String message = 'Validation Failed']) - : super(github, message); + const ValidationFailed(super.github, + [String super.message = 'Validation Failed']); } diff --git a/lib/src/common/util/json.dart b/lib/src/common/util/json.dart index 5912bfe9..ff922b0b 100644 --- a/lib/src/common/util/json.dart +++ b/lib/src/common/util/json.dart @@ -38,7 +38,7 @@ class GitHubJson { /// and it also deletes keys associated with null values in maps before converting them. /// /// The obtained String can be decoded using `jsonDecode`. - static String encode(Object object, {String indent}) { + static String encode(Object object, {String? indent}) { final encoder = JsonEncoder.withIndent(indent, _toEncodable); return encoder.convert(_checkObject(object)); } diff --git a/lib/src/common/util/oauth2.dart b/lib/src/common/util/oauth2.dart index 54788d1c..9333d606 100644 --- a/lib/src/common/util/oauth2.dart +++ b/lib/src/common/util/oauth2.dart @@ -8,7 +8,7 @@ import 'package:http/http.dart' as http; /// **Example**: /// /// var flow = new OAuth2Flow('ClientID', 'ClientSecret'); -/// var authUrl = flow.createAuthorizationURL(); +/// var authUrl = flow.createAuthorizeUrl(); /// // Display to the User and handle the redirect URI, and also get the code. /// flow.exchange(code).then((response) { /// var github = new GitHub(auth: new Authentication.withToken(response.token)); @@ -24,10 +24,10 @@ class OAuth2Flow { final List scopes; /// Redirect URI - final String redirectUri; + final String? redirectUri; /// State - final String state; + final String? state; /// Client Secret final String clientSecret; @@ -35,10 +35,10 @@ class OAuth2Flow { /// OAuth2 Base URL final String baseUrl; - GitHub github; + GitHub? github; OAuth2Flow(this.clientId, this.clientSecret, - {String redirectUri, + {String? redirectUri, this.scopes = const [], this.state, this.github, @@ -54,18 +54,16 @@ class OAuth2Flow { /// /// This should be displayed to the user. String createAuthorizeUrl() { - return baseUrl + - '/authorize' + - buildQueryString({ + return '$baseUrl/authorize${buildQueryString({ 'client_id': clientId, 'scope': scopes.join(','), 'redirect_uri': redirectUri, 'state': state - }); + })}'; } /// Exchanges the given [code] for a token. - Future exchange(String code, [String origin]) { + Future exchange(String code, [String? origin]) { final headers = { 'Accept': 'application/json', 'content-type': 'application/json' @@ -82,8 +80,8 @@ class OAuth2Flow { 'redirect_uri': redirectUri }); - return (github == null ? http.Client() : github.client) - .post('$baseUrl/access_token', body: body, headers: headers) + return (github == null ? http.Client() : github!.client) + .post(Uri.parse('$baseUrl/access_token'), body: body, headers: headers) .then((response) { final json = jsonDecode(response.body) as Map; if (json['error'] != null) { @@ -97,9 +95,9 @@ class OAuth2Flow { /// Represents a response for exchanging a code for a token. class ExchangeResponse { - final String token; + final String? token; final List scopes; - final String tokenType; + final String? tokenType; ExchangeResponse(this.token, this.tokenType, this.scopes); } diff --git a/lib/src/common/util/pagination.dart b/lib/src/common/util/pagination.dart index 2e371835..93f3a0d7 100644 --- a/lib/src/common/util/pagination.dart +++ b/lib/src/common/util/pagination.dart @@ -12,10 +12,10 @@ class PaginationHelper { PaginationHelper(this.github); Stream fetchStreamed(String method, String path, - {int pages, - Map headers, - Map params, - String body, + {int? pages, + Map? headers, + Map? params, + String? body, int statusCode = 200}) async* { var count = 0; const serverErrorBackOff = Duration(seconds: 10); @@ -27,9 +27,7 @@ class PaginationHelper { } else { params = Map.from(params); } - assert(!params.containsKey('page')); - // ignore: literal_only_boolean_expressions while (true) { http.Response response; try { @@ -62,9 +60,6 @@ class PaginationHelper { } final info = parseLinkHeader(link); - if (info == null) { - break; - } final next = info['next']; @@ -72,23 +67,21 @@ class PaginationHelper { break; } - final nextUrl = Uri.parse(next); - final nextPageArg = nextUrl.queryParameters['page']; - assert(nextPageArg != null); - params['page'] = nextPageArg; + path = next; + params = null; } } Stream jsonObjects( String method, String path, { - int pages, - Map headers, - Map params, - String body, + int? pages, + Map? headers, + Map? params, + String? body, int statusCode = 200, - String preview, - String arrayKey, + String? preview, + String? arrayKey, }) async* { headers ??= {}; if (preview != null) { @@ -106,11 +99,11 @@ class PaginationHelper { statusCode: statusCode, )) { final json = arrayKey == null - ? jsonDecode(response.body) as List + ? jsonDecode(response.body) as List? : (jsonDecode(response.body) as Map)[arrayKey]; for (final item in json) { - yield item as T; + yield (item as T?)!; } } } @@ -121,13 +114,13 @@ class PaginationHelper { String method, String path, JSONConverter converter, { - int pages, - Map headers, - Map params, - String body, + int? pages, + Map? headers, + Map? params, + String? body, int statusCode = 200, - String preview, - String arrayKey, + String? preview, + String? arrayKey, }) { return jsonObjects( method, diff --git a/lib/src/common/util/utils.dart b/lib/src/common/util/utils.dart index 92fdb7a2..5c690774 100644 --- a/lib/src/common/util/utils.dart +++ b/lib/src/common/util/utils.dart @@ -1,3 +1,5 @@ +// ignore_for_file: constant_identifier_names + import 'package:github/src/common.dart'; import 'package:meta/meta.dart'; @@ -5,21 +7,21 @@ import 'package:meta/meta.dart'; /// but with a String value that is used for serialization. @immutable abstract class EnumWithValue { - final String value; + final String? value; /// The value will be used when [toJson] or [toString] will be called. /// It will also be used to check if two [EnumWithValue] are equal. const EnumWithValue(this.value); @override - String toString() => value; + String toString() => value ?? 'null'; /// Returns the String value of this. - String toJson() => value; + String toJson() => value ?? 'null'; /// True iff [other] is an [EnumWithValue] with the same value as this object. @override - bool operator ==(dynamic other) => + bool operator ==(Object other) => other is EnumWithValue && value == other.value; @override @@ -45,7 +47,7 @@ class OnlyWhen { /// Converts the [date] to GitHub's ISO-8601 format: /// /// The format is "YYYY-MM-DDTHH:mm:ssZ" -String dateToGitHubIso8601(DateTime date) { +String? dateToGitHubIso8601(DateTime? date) { if (date == null) { return null; } @@ -60,6 +62,7 @@ RepositorySlug slugFromAPIUrl(String url) { return RepositorySlug(parts[0], parts[1]); } +// ignore: avoid_classes_with_only_static_members abstract class StatusCodes { static const int OK = 200; static const int CREATED = 201; @@ -143,24 +146,15 @@ List> mapToList(Map input) { return out; } -/// Internal method to handle null for parsing dates. -DateTime parseDateTime(String input) { - if (input == null) { - return null; - } - - return DateTime.parse(input); -} - /// Returns a new map containing only the entries of [input] whose value is not null. /// /// If [recursive] is true, nested maps are also filtered. -Map createNonNullMap(Map input, {bool recursive = true}) { - final map = {}; +Map createNonNullMap(Map input, {bool recursive = true}) { + final map = {}; for (final entry in input.entries) { if (entry.value != null) { map[entry.key] = recursive && entry.value is Map - ? createNonNullMap(entry.value as Map, recursive: recursive) + ? createNonNullMap(entry.value as Map, recursive: recursive) as V? : entry.value; } } @@ -182,7 +176,7 @@ int parseFancyNumber(String input) { } else { final m = multipliers.keys.firstWhere((m) => input.endsWith(m)); input = input.substring(0, input.length - m.length); - value = num.parse(input) * multipliers[m]; + value = num.parse(input) * multipliers[m]! as int; } return value; diff --git a/lib/src/common/xplat_common.dart b/lib/src/common/xplat_common.dart index 5d7e7226..1c60f906 100644 --- a/lib/src/common/xplat_common.dart +++ b/lib/src/common/xplat_common.dart @@ -10,13 +10,13 @@ import 'package:github/src/common.dart'; /// In both contexts it delegates to [findAuthenticationInMap] to find the /// github token or username and password. Authentication findAuthenticationFromEnvironment() => - Authentication.anonymous(); + const Authentication.anonymous(); /// Checks the passed in map for keys in [COMMON_GITHUB_TOKEN_ENV_KEYS]. /// The first one that exists is used as the github token to call [Authentication.withToken] with. /// If the above fails, the GITHUB_USERNAME and GITHUB_PASSWORD keys will be checked. /// If those keys both exist, then [Authentication.basic] will be used. -Authentication findAuthenticationInMap(Map map) { +Authentication? findAuthenticationInMap(Map map) { for (final key in COMMON_GITHUB_TOKEN_ENV_KEYS) { if (map.containsKey(key)) { return Authentication.withToken(map[key]); diff --git a/lib/src/const/language_color.dart b/lib/src/const/language_color.dart index 9a3fd526..95bfc175 100644 --- a/lib/src/const/language_color.dart +++ b/lib/src/const/language_color.dart @@ -1,195 +1,231 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -// VERSION OF 2020-02-08T14:23:59.127442 +// VERSION OF 2022-03-13T22:39:42.882755 -const languagesColor = { +const languageColors = { '1C Enterprise': '#814CCC', - '4D': '#EDEDED', + '2-Dimensional Array': '#38761D', + '4D': '#004289', 'ABAP': '#E8274B', + 'ABAP CDS': '#555E25', 'ABNF': '#EDEDED', 'AGS Script': '#B9D9FF', + 'AIDL': '#34EB6B', + 'AL': '#3AA2B5', 'AMPL': '#E6EFBB', 'ANTLR': '#9DC3FF', 'API Blueprint': '#2ACCA8', 'APL': '#5A8164', + 'ASL': '#EDEDED', 'ASN.1': '#EDEDED', - 'ASP': '#6A40FD', + 'ASP.NET': '#9400FF', 'ATS': '#1AC620', 'ActionScript': '#882B0F', 'Ada': '#02F88C', - 'Adobe Font Metrics': '#EDEDED', + 'Adobe Font Metrics': '#FA0F00', 'Agda': '#315665', 'Alloy': '#64C800', - 'Alpine Abuild': '#EDEDED', - 'Altium Designer': '#EDEDED', + 'Alpine Abuild': '#0D597F', + 'Altium Designer': '#A89663', 'AngelScript': '#C7D7DC', - 'Ant Build System': '#EDEDED', - 'ApacheConf': '#EDEDED', - 'Apex': '#EDEDED', - 'Apollo Guidance Computer': '#EDEDED', + 'Ant Build System': '#A9157E', + 'ApacheConf': '#D12127', + 'Apex': '#1797C0', + 'Apollo Guidance Computer': '#0B3D91', 'AppleScript': '#101F1F', 'Arc': '#AA2AFE', - 'AsciiDoc': '#EDEDED', + 'AsciiDoc': '#73A0C5', 'AspectJ': '#A957B0', 'Assembly': '#6E4C13', - 'Asymptote': '#4A0C0C', - 'Augeas': '#EDEDED', + 'Astro': '#FF5A03', + 'Asymptote': '#FF0000', + 'Augeas': '#9CC134', 'AutoHotkey': '#6594B9', 'AutoIt': '#1C3552', - 'Awk': '#EDEDED', + 'Avro IDL': '#0040FF', + 'Awk': '#C30E9B', + 'BASIC': '#FF0000', 'Ballerina': '#FF5000', 'Batchfile': '#C1F12E', + 'Beef': '#A52F4E', 'Befunge': '#EDEDED', - 'BibTeX': '#EDEDED', - 'Bison': '#EDEDED', - 'BitBake': '#EDEDED', - 'Blade': '#EDEDED', - 'BlitzBasic': '#EDEDED', + 'BibTeX': '#778899', + 'Bicep': '#519ABA', + 'Bison': '#6A463F', + 'BitBake': '#00BCE4', + 'Blade': '#F7523F', + 'BlitzBasic': '#00FFAE', 'BlitzMax': '#CD6400', - 'Bluespec': '#EDEDED', + 'Bluespec': '#12223C', 'Boo': '#D4BEC1', + 'Boogie': '#C80FA0', 'Brainfuck': '#2F2530', - 'Brightscript': '#EDEDED', + 'Brightscript': '#662D91', + 'Browserslist': '#FFD539', 'C': '#555555', 'C#': '#178600', 'C++': '#F34B7D', 'C-ObjDump': '#EDEDED', 'C2hs Haskell': '#EDEDED', - 'CLIPS': '#EDEDED', - 'CMake': '#EDEDED', + 'CIL': '#EDEDED', + 'CLIPS': '#00A300', + 'CMake': '#DA3434', 'COBOL': '#EDEDED', - 'COLLADA': '#EDEDED', - 'CSON': '#EDEDED', + 'CODEOWNERS': '#EDEDED', + 'COLLADA': '#F1A42B', + 'CSON': '#244776', 'CSS': '#563D7C', - 'CSV': '#EDEDED', - 'CWeb': '#EDEDED', - 'Cabal Config': '#EDEDED', - 'Cap\'n Proto': '#EDEDED', + 'CSV': '#237346', + 'CUE': '#5886E1', + 'CWeb': '#00007A', + 'Cabal Config': '#483465', + 'Cairo': '#FF4A48', + 'Cap\'n Proto': '#C42727', 'CartoCSS': '#EDEDED', 'Ceylon': '#DFA535', 'Chapel': '#8DC63F', 'Charity': '#EDEDED', - 'ChucK': '#EDEDED', + 'ChucK': '#3F8000', 'Cirru': '#CCCCFF', 'Clarion': '#DB901E', + 'Clarity': '#5546FF', + 'Classic ASP': '#6A40FD', 'Clean': '#3F85AF', 'Click': '#E4E6F3', 'Clojure': '#DB5855', - 'Closure Templates': '#EDEDED', - 'Cloud Firestore Security Rules': '#EDEDED', + 'Closure Templates': '#0D948F', + 'Cloud Firestore Security Rules': '#FFA000', 'CoNLL-U': '#EDEDED', + 'CodeQL': '#140F46', 'CoffeeScript': '#244776', 'ColdFusion': '#ED2CD6', - 'ColdFusion CFC': '#EDEDED', + 'ColdFusion CFC': '#ED2CD6', 'Common Lisp': '#3FB68B', 'Common Workflow Language': '#B5314C', 'Component Pascal': '#B0CE4E', 'Cool': '#EDEDED', - 'Coq': '#EDEDED', + 'Coq': '#D0B68C', 'Cpp-ObjDump': '#EDEDED', 'Creole': '#EDEDED', 'Crystal': '#000100', - 'Csound': '#EDEDED', - 'Csound Document': '#EDEDED', - 'Csound Score': '#EDEDED', + 'Csound': '#1A1A1A', + 'Csound Document': '#1A1A1A', + 'Csound Score': '#1A1A1A', 'Cuda': '#3A4E3A', + 'Cue Sheet': '#EDEDED', + 'Curry': '#531242', 'Cycript': '#EDEDED', - 'Cython': '#EDEDED', + 'Cython': '#FEDF5B', 'D': '#BA595E', 'D-ObjDump': '#EDEDED', 'DIGITAL Command Language': '#EDEDED', 'DM': '#447265', 'DNS Zone': '#EDEDED', 'DTrace': '#EDEDED', - 'Darcs Patch': '#EDEDED', + 'Dafny': '#FFEC25', + 'Darcs Patch': '#8EFF23', 'Dart': '#00B4AB', 'DataWeave': '#003A52', + 'Debian Package Control File': '#D70751', + 'DenizenScript': '#FBEE96', 'Dhall': '#DFAFFF', 'Diff': '#EDEDED', - 'DirectX 3D File': '#EDEDED', + 'DirectX 3D File': '#AACE60', 'Dockerfile': '#384D54', 'Dogescript': '#CCA760', 'Dylan': '#6C616E', 'E': '#CCCE35', + 'E-mail': '#EDEDED', 'EBNF': '#EDEDED', 'ECL': '#8A1267', - 'ECLiPSe': '#EDEDED', - 'EJS': '#EDEDED', - 'EML': '#EDEDED', + 'ECLiPSe': '#001D9D', + 'EJS': '#A91E50', 'EQ': '#A78649', 'Eagle': '#EDEDED', - 'Easybuild': '#EDEDED', - 'Ecere Projects': '#EDEDED', - 'EditorConfig': '#EDEDED', + 'Earthly': '#2AF0FF', + 'Easybuild': '#069406', + 'Ecere Projects': '#913960', + 'EditorConfig': '#FFF1F2', 'Edje Data Collection': '#EDEDED', - 'Eiffel': '#946D57', + 'Eiffel': '#4D6977', 'Elixir': '#6E4A7E', 'Elm': '#60B5CC', 'Emacs Lisp': '#C065DB', 'EmberScript': '#FFF4F3', 'Erlang': '#B83998', + 'Euphoria': '#FF790B', 'F#': '#B845FC', 'F*': '#572E30', - 'FIGlet Font': '#EDEDED', + 'FIGlet Font': '#FFDDBB', 'FLUX': '#88CCFF', 'Factor': '#636746', 'Fancy': '#7B9DB4', 'Fantom': '#14253C', 'Faust': '#C37240', - 'Filebench WML': '#EDEDED', + 'Fennel': '#FFF3D7', + 'Filebench WML': '#F6B900', 'Filterscript': '#EDEDED', + 'Fluent': '#FFCC33', 'Formatted': '#EDEDED', 'Forth': '#341708', 'Fortran': '#4D41B1', + 'Fortran Free Form': '#4D41B1', + 'FreeBasic': '#867DB1', 'FreeMarker': '#0050B2', 'Frege': '#00CAFE', + 'Futhark': '#5F021F', 'G-code': '#D08CF2', 'GAML': '#FFC766', - 'GAMS': '#EDEDED', - 'GAP': '#EDEDED', - 'GCC Machine Description': '#EDEDED', + 'GAMS': '#F49A22', + 'GAP': '#0000CC', + 'GCC Machine Description': '#FFCFAB', 'GDB': '#EDEDED', 'GDScript': '#355570', - 'GLSL': '#EDEDED', + 'GEDCOM': '#003058', + 'GLSL': '#5686A5', 'GN': '#EDEDED', + 'GSC': '#FF6800', 'Game Maker Language': '#71B417', + 'Gemfile.lock': '#701516', 'Genie': '#FB855D', - 'Genshi': '#EDEDED', - 'Gentoo Ebuild': '#EDEDED', - 'Gentoo Eclass': '#EDEDED', - 'Gerber Image': '#EDEDED', + 'Genshi': '#951531', + 'Gentoo Ebuild': '#9400FF', + 'Gentoo Eclass': '#9400FF', + 'Gerber Image': '#D20B00', 'Gettext Catalog': '#EDEDED', 'Gherkin': '#5B2063', - 'Git Attributes': '#EDEDED', - 'Git Config': '#EDEDED', + 'Git Attributes': '#F44D27', + 'Git Config': '#F44D27', + 'Gleam': '#FFAFF3', 'Glyph': '#C1AC7F', 'Glyph Bitmap Distribution Format': '#EDEDED', 'Gnuplot': '#F0A9F0', 'Go': '#00ADD8', + 'Go Checksums': '#00ADD8', + 'Go Module': '#00ADD8', 'Golo': '#88562A', 'Gosu': '#82937F', - 'Grace': '#EDEDED', - 'Gradle': '#EDEDED', - 'Grammatical Framework': '#79AA7A', + 'Grace': '#615F8B', + 'Gradle': '#02303A', + 'Grammatical Framework': '#FF0000', 'Graph Modeling Language': '#EDEDED', - 'GraphQL': '#EDEDED', - 'Graphviz (DOT)': '#EDEDED', - 'Groovy': '#E69F56', - 'Groovy Server Pages': '#EDEDED', - 'HAProxy': '#EDEDED', + 'GraphQL': '#E10098', + 'Graphviz (DOT)': '#2596BE', + 'Groovy': '#4298B8', + 'Groovy Server Pages': '#4298B8', + 'HAProxy': '#106DA9', 'HCL': '#EDEDED', - 'HLSL': '#EDEDED', + 'HLSL': '#AACE60', 'HTML': '#E34C26', - 'HTML+Django': '#EDEDED', - 'HTML+ECR': '#EDEDED', - 'HTML+EEX': '#EDEDED', - 'HTML+ERB': '#EDEDED', - 'HTML+PHP': '#EDEDED', - 'HTML+Razor': '#EDEDED', - 'HTTP': '#EDEDED', - 'HXML': '#EDEDED', + 'HTML+ECR': '#2E1052', + 'HTML+EEX': '#6E4A7E', + 'HTML+ERB': '#701516', + 'HTML+PHP': '#4F5D95', + 'HTML+Razor': '#512BE4', + 'HTTP': '#005C9C', + 'HXML': '#F68712', 'Hack': '#878787', - 'Haml': '#EDEDED', - 'Handlebars': '#EDEDED', + 'Haml': '#ECE2A9', + 'Handlebars': '#F7931E', 'Harbour': '#0E60E3', 'Haskell': '#5E5086', 'Haxe': '#DF7900', @@ -199,64 +235,72 @@ const languagesColor = { 'HyPhy': '#EDEDED', 'IDL': '#A3522F', 'IGOR Pro': '#0000CC', - 'INI': '#EDEDED', + 'INI': '#D1DBE0', 'IRC log': '#EDEDED', 'Idris': '#B30000', - 'Ignore List': '#EDEDED', + 'Ignore List': '#000000', + 'ImageJ Macro': '#99AAFF', 'Inform 7': '#EDEDED', - 'Inno Setup': '#EDEDED', + 'Inno Setup': '#264B99', 'Io': '#A9188D', 'Ioke': '#078193', 'Isabelle': '#FEFE00', - 'Isabelle ROOT': '#EDEDED', + 'Isabelle ROOT': '#FEFE00', 'J': '#9EEDFF', - 'JFlex': '#EDEDED', - 'JSON': '#EDEDED', - 'JSON with Comments': '#EDEDED', - 'JSON5': '#EDEDED', - 'JSONLD': '#EDEDED', + 'JAR Manifest': '#B07219', + 'JFlex': '#DBCA00', + 'JSON': '#292929', + 'JSON with Comments': '#292929', + 'JSON5': '#267CB9', + 'JSONLD': '#0C479C', 'JSONiq': '#40D47E', - 'JSX': '#EDEDED', - 'Jasmin': '#EDEDED', + 'Janet': '#0886A5', + 'Jasmin': '#D03600', 'Java': '#B07219', - 'Java Properties': '#EDEDED', - 'Java Server Pages': '#EDEDED', + 'Java Properties': '#2A6277', + 'Java Server Pages': '#2A6277', 'JavaScript': '#F1E05A', - 'JavaScript+ERB': '#EDEDED', - 'Jison': '#EDEDED', - 'Jison Lex': '#EDEDED', + 'JavaScript+ERB': '#F1E05A', + 'Jest Snapshot': '#15C213', + 'Jinja': '#A52A22', + 'Jison': '#56B3CB', + 'Jison Lex': '#56B3CB', 'Jolie': '#843179', 'Jsonnet': '#0064BD', 'Julia': '#A270BA', 'Jupyter Notebook': '#DA5B0B', 'KRL': '#28430A', - 'KiCad Layout': '#EDEDED', - 'KiCad Legacy Layout': '#EDEDED', - 'KiCad Schematic': '#EDEDED', + 'Kaitai Struct': '#773B37', + 'KakouneScript': '#6F8042', + 'KiCad Layout': '#2F4AAB', + 'KiCad Legacy Layout': '#2F4AAB', + 'KiCad Schematic': '#2F4AAB', 'Kit': '#EDEDED', - 'Kotlin': '#F18E33', + 'Kotlin': '#A97BFF', + 'Kusto': '#EDEDED', 'LFE': '#4C3023', 'LLVM': '#185619', 'LOLCODE': '#CC9900', 'LSL': '#3D9970', 'LTspice Symbol': '#EDEDED', - 'LabVIEW': '#EDEDED', + 'LabVIEW': '#FEDE06', + 'Lark': '#2980B9', 'Lasso': '#999999', - 'Latte': '#EDEDED', + 'Latte': '#F2A542', 'Lean': '#EDEDED', - 'Less': '#EDEDED', + 'Less': '#1D365D', 'Lex': '#DBCA00', - 'LilyPond': '#EDEDED', + 'LilyPond': '#9CCC7C', 'Limbo': '#EDEDED', 'Linker Script': '#EDEDED', 'Linux Kernel Module': '#EDEDED', - 'Liquid': '#EDEDED', - 'Literate Agda': '#EDEDED', - 'Literate CoffeeScript': '#EDEDED', - 'Literate Haskell': '#EDEDED', + 'Liquid': '#67B8DE', + 'Literate Agda': '#315665', + 'Literate CoffeeScript': '#244776', + 'Literate Haskell': '#5E5086', 'LiveScript': '#499886', 'Logos': '#EDEDED', - 'Logtalk': '#EDEDED', + 'Logtalk': '#295B9A', 'LookML': '#652B81', 'LoomScript': '#EDEDED', 'Lua': '#000080', @@ -270,35 +314,43 @@ const languagesColor = { 'MQL5': '#4A76B8', 'MTML': '#B7E1F4', 'MUF': '#EDEDED', + 'Macaulay2': '#D8FFFF', 'Makefile': '#427819', - 'Mako': '#EDEDED', - 'Markdown': '#EDEDED', - 'Marko': '#EDEDED', + 'Mako': '#7E858D', + 'Markdown': '#083FA1', + 'Marko': '#42BFF2', 'Mask': '#F97732', - 'Mathematica': '#EDEDED', + 'Mathematica': '#DD1100', 'Maven POM': '#EDEDED', 'Max': '#C4A79C', - 'MediaWiki': '#EDEDED', 'Mercury': '#FF2B2B', 'Meson': '#007800', 'Metal': '#8F14E9', 'Microsoft Developer Studio Project': '#EDEDED', + 'Microsoft Visual Studio Solution': '#EDEDED', 'MiniD': '#EDEDED', + 'MiniYAML': '#FF1111', + 'Mint': '#02B046', 'Mirah': '#C7A938', - 'Modelica': '#EDEDED', - 'Modula-2': '#EDEDED', + 'Modelica': '#DE1D31', + 'Modula-2': '#10253F', 'Modula-3': '#223388', 'Module Management System': '#EDEDED', 'Monkey': '#EDEDED', 'Moocode': '#EDEDED', - 'MoonScript': '#EDEDED', - 'Motorola 68K Assembly': '#EDEDED', + 'MoonScript': '#FF4585', + 'Motoko': '#FBB03B', + 'Motorola 68K Assembly': '#005DAA', 'Muse': '#EDEDED', + 'Mustache': '#724B3B', 'Myghty': '#EDEDED', + 'NASL': '#EDEDED', 'NCL': '#28431F', + 'NEON': '#EDEDED', 'NL': '#EDEDED', - 'NPM Config': '#EDEDED', + 'NPM Config': '#CB3837', 'NSIS': '#EDEDED', + 'NWScript': '#111522', 'Nearley': '#990000', 'Nemerle': '#3D3C6E', 'NetLinx': '#0AA0FF', @@ -306,13 +358,14 @@ const languagesColor = { 'NetLogo': '#FF6375', 'NewLisp': '#87AED7', 'Nextflow': '#3AC486', - 'Nginx': '#EDEDED', - 'Nim': '#37775B', + 'Nginx': '#009639', + 'Nim': '#FFC200', 'Ninja': '#EDEDED', 'Nit': '#009917', 'Nix': '#7E7EFF', 'Nu': '#C9DF40', - 'NumPy': '#EDEDED', + 'NumPy': '#9C8AF9', + 'Nunjucks': '#3D8137', 'OCaml': '#3BE133', 'ObjDump': '#EDEDED', 'Object Data Instance Notation': '#EDEDED', @@ -324,22 +377,24 @@ const languagesColor = { 'Omgrofl': '#CABBFF', 'Opa': '#EDEDED', 'Opal': '#F7EDE0', - 'Open Policy Agent': '#EDEDED', - 'OpenCL': '#EDEDED', - 'OpenEdge ABL': '#EDEDED', + 'Open Policy Agent': '#7D9199', + 'OpenCL': '#ED2E2D', + 'OpenEdge ABL': '#5CE600', + 'OpenQASM': '#AA70FF', 'OpenRC runscript': '#EDEDED', - 'OpenSCAD': '#EDEDED', + 'OpenSCAD': '#E5CD45', 'OpenStep Property List': '#EDEDED', 'OpenType Feature File': '#EDEDED', - 'Org': '#EDEDED', + 'Org': '#77AA99', 'Ox': '#EDEDED', 'Oxygene': '#CDD0E3', 'Oz': '#FAB738', 'P4': '#7055B5', + 'PEG.js': '#234D6B', 'PHP': '#4F5D95', 'PLSQL': '#DAD8D8', - 'PLpgSQL': '#EDEDED', - 'POV-Ray SDL': '#EDEDED', + 'PLpgSQL': '#336790', + 'POV-Ray SDL': '#6BAC65', 'Pan': '#CC0000', 'Papyrus': '#6600CC', 'Parrot': '#F3CA0A', @@ -351,43 +406,49 @@ const languagesColor = { 'Perl': '#0298C3', 'Pic': '#EDEDED', 'Pickle': '#EDEDED', - 'PicoLisp': '#EDEDED', + 'PicoLisp': '#6067AF', 'PigLatin': '#FCD7DE', 'Pike': '#005390', + 'PlantUML': '#EDEDED', 'Pod': '#EDEDED', 'Pod 6': '#EDEDED', 'PogoScript': '#D80074', 'Pony': '#EDEDED', - 'PostCSS': '#EDEDED', + 'PostCSS': '#DC3A0C', 'PostScript': '#DA291C', 'PowerBuilder': '#8F0F8D', 'PowerShell': '#012456', - 'Prisma': '#EDEDED', + 'Prisma': '#0C344B', 'Processing': '#0096D8', + 'Procfile': '#3B2F63', 'Proguard': '#EDEDED', 'Prolog': '#74283C', + 'Promela': '#DE0000', 'Propeller Spin': '#7FA2A7', 'Protocol Buffer': '#EDEDED', + 'Protocol Buffer Text Format': '#EDEDED', 'Public Key': '#EDEDED', - 'Pug': '#EDEDED', + 'Pug': '#A86454', 'Puppet': '#302B6D', 'Pure Data': '#EDEDED', 'PureBasic': '#5A6986', 'PureScript': '#1D222D', 'Python': '#3572A5', - 'Python console': '#EDEDED', - 'Python traceback': '#EDEDED', + 'Python console': '#3572A5', + 'Python traceback': '#3572A5', + 'Q#': '#FED659', 'QML': '#44A51C', 'QMake': '#EDEDED', + 'Qt Script': '#00B841', 'Quake': '#882233', 'R': '#198CE7', 'RAML': '#77D9FB', - 'RDoc': '#EDEDED', + 'RDoc': '#701516', 'REALbasic': '#EDEDED', - 'REXX': '#EDEDED', - 'RHTML': '#EDEDED', - 'RMarkdown': '#EDEDED', + 'REXX': '#D90E09', + 'RMarkdown': '#198CE7', 'RPC': '#EDEDED', + 'RPGLE': '#2BDE21', 'RPM Spec': '#EDEDED', 'RUNOFF': '#665A4E', 'Racket': '#3C5CAA', @@ -395,75 +456,85 @@ const languagesColor = { 'Raku': '#0000FB', 'Rascal': '#FFFAA0', 'Raw token data': '#EDEDED', + 'ReScript': '#ED5051', 'Readline Config': '#EDEDED', 'Reason': '#FF5847', 'Rebol': '#358A5B', + 'Record Jar': '#0673BA', 'Red': '#F50000', 'Redcode': '#EDEDED', - 'Regular Expression': '#EDEDED', + 'Redirect Rules': '#EDEDED', + 'Regular Expression': '#009A00', 'Ren\'Py': '#FF7F7F', 'RenderScript': '#EDEDED', 'Rich Text Format': '#EDEDED', 'Ring': '#2D54CB', 'Riot': '#A71E49', - 'RobotFramework': '#EDEDED', + 'RobotFramework': '#00C0B5', 'Roff': '#ECDEBE', - 'Roff Manpage': '#EDEDED', + 'Roff Manpage': '#ECDEBE', 'Rouge': '#CC0088', 'Ruby': '#701516', 'Rust': '#DEA584', 'SAS': '#B34936', - 'SCSS': '#EDEDED', + 'SCSS': '#C6538C', + 'SELinux Policy': '#EDEDED', 'SMT': '#EDEDED', - 'SPARQL': '#EDEDED', + 'SPARQL': '#0C4597', 'SQF': '#3F3F3F', - 'SQL': '#EDEDED', - 'SQLPL': '#EDEDED', + 'SQL': '#E38C00', + 'SQLPL': '#E38C00', 'SRecode Template': '#348A34', 'SSH Config': '#EDEDED', 'STON': '#EDEDED', - 'SVG': '#EDEDED', + 'SVG': '#FF9900', 'SWIG': '#EDEDED', 'Sage': '#EDEDED', 'SaltStack': '#646464', - 'Sass': '#EDEDED', + 'Sass': '#A53B70', 'Scala': '#C22D40', - 'Scaml': '#EDEDED', + 'Scaml': '#BD181A', 'Scheme': '#1E4AEC', - 'Scilab': '#EDEDED', + 'Scilab': '#CA0F21', 'Self': '#0579AA', - 'ShaderLab': '#EDEDED', + 'ShaderLab': '#222C37', 'Shell': '#89E051', + 'ShellCheck Config': '#CECFCB', 'ShellSession': '#EDEDED', 'Shen': '#120F14', + 'Sieve': '#EDEDED', + 'Singularity': '#64E6AD', 'Slash': '#007EFF', 'Slice': '#003FA2', - 'Slim': '#EDEDED', + 'Slim': '#2B2B2B', 'SmPL': '#C94949', 'Smali': '#EDEDED', 'Smalltalk': '#596706', - 'Smarty': '#EDEDED', + 'Smarty': '#F0C040', 'Solidity': '#AA6746', - 'SourcePawn': '#5C7611', + 'Soong': '#EDEDED', + 'SourcePawn': '#F69E1D', 'Spline Font Database': '#EDEDED', 'Squirrel': '#800000', 'Stan': '#B2011D', 'Standard ML': '#DC566D', 'Starlark': '#76D275', - 'Stata': '#EDEDED', - 'Stylus': '#EDEDED', - 'SubRip Text': '#EDEDED', - 'SugarSS': '#EDEDED', + 'Stata': '#1A5F91', + 'StringTemplate': '#3FB34F', + 'Stylus': '#FF6347', + 'SubRip Text': '#9E0101', + 'SugarSS': '#2FCC9F', 'SuperCollider': '#46390B', - 'Svelte': '#EDEDED', - 'Swift': '#FFAC45', + 'Svelte': '#FF3E00', + 'Swift': '#F05138', 'SystemVerilog': '#DAE1C2', 'TI Program': '#A0AA87', - 'TLA': '#EDEDED', - 'TOML': '#EDEDED', - 'TSQL': '#EDEDED', - 'TSX': '#EDEDED', - 'TXL': '#EDEDED', + 'TLA': '#4B0079', + 'TOML': '#9C4221', + 'TSQL': '#E38C00', + 'TSV': '#237346', + 'TSX': '#2B7489', + 'TXL': '#0178B8', 'Tcl': '#E4CC98', 'Tcsh': '#EDEDED', 'TeX': '#3D6117', @@ -471,57 +542,64 @@ const languagesColor = { 'Terra': '#00004C', 'Texinfo': '#EDEDED', 'Text': '#EDEDED', - 'Textile': '#EDEDED', - 'Thrift': '#EDEDED', + 'TextMate Properties': '#DF66E4', + 'Textile': '#FFE7AC', + 'Thrift': '#D12127', 'Turing': '#CF142B', 'Turtle': '#EDEDED', - 'Twig': '#EDEDED', + 'Twig': '#C1D026', 'Type Language': '#EDEDED', 'TypeScript': '#2B7489', - 'Unified Parallel C': '#EDEDED', - 'Unity3D Asset': '#EDEDED', + 'Unified Parallel C': '#4E3617', + 'Unity3D Asset': '#222C37', 'Unix Assembly': '#EDEDED', - 'Uno': '#EDEDED', + 'Uno': '#9933CC', 'UnrealScript': '#A54C4D', - 'UrWeb': '#EDEDED', - 'V': '#5D87BD', + 'UrWeb': '#CCCCEE', + 'V': '#4F87C4', 'VBA': '#867DB1', 'VBScript': '#15DCDC', 'VCL': '#148AA8', 'VHDL': '#ADB2CB', 'Vala': '#FBE5CD', + 'Valve Data Format': '#F26025', 'Verilog': '#B2B7F8', - 'Vim Snippet': '#EDEDED', - 'Vim script': '#199F4B', + 'Vim Help File': '#199F4B', + 'Vim Script': '#199F4B', + 'Vim Snippet': '#199F4B', 'Visual Basic .NET': '#945DB7', 'Volt': '#1F1F1F', - 'Vue': '#2C3E50', + 'Vue': '#41B883', + 'Vyper': '#2980B9', 'Wavefront Material': '#EDEDED', 'Wavefront Object': '#EDEDED', - 'Web Ontology Language': '#EDEDED', + 'Web Ontology Language': '#5B70BD', 'WebAssembly': '#04133B', 'WebIDL': '#EDEDED', 'WebVTT': '#EDEDED', 'Wget Config': '#EDEDED', - 'Windows Registry Entries': '#EDEDED', + 'Wikitext': '#FC5757', + 'Windows Registry Entries': '#52D5FF', + 'Witcher Script': '#FF0000', 'Wollok': '#A23738', - 'World of Warcraft Addon Data': '#EDEDED', + 'World of Warcraft Addon Data': '#F7E43F', 'X BitMap': '#EDEDED', 'X Font Directory Index': '#EDEDED', 'X PixMap': '#EDEDED', 'X10': '#4B6BEF', 'XC': '#99DA07', 'XCompose': '#EDEDED', - 'XML': '#EDEDED', - 'XML Property List': '#EDEDED', + 'XML': '#0060AC', + 'XML Property List': '#0060AC', 'XPages': '#EDEDED', 'XProc': '#EDEDED', 'XQuery': '#5232E7', 'XS': '#EDEDED', 'XSLT': '#EB8CEB', - 'Xojo': '#EDEDED', - 'Xtend': '#EDEDED', - 'YAML': '#EDEDED', + 'Xojo': '#81BD41', + 'Xonsh': '#285EEF', + 'Xtend': '#24255D', + 'YAML': '#CB171E', 'YANG': '#EDEDED', 'YARA': '#220000', 'YASnippet': '#32AB90', @@ -532,21 +610,25 @@ const languagesColor = { 'ZenScript': '#00BCD1', 'Zephir': '#118F9E', 'Zig': '#EC915C', - 'Zimpl': '#EDEDED', + 'Zimpl': '#D67711', 'cURL Config': '#EDEDED', 'desktop': '#EDEDED', 'dircolors': '#EDEDED', 'eC': '#913960', 'edn': '#EDEDED', - 'fish': '#EDEDED', - 'mIRC Script': '#926059', + 'fish': '#4AAE47', + 'hoon': '#00B171', + 'jq': '#C7254E', + 'kvlang': '#1DA6E0', + 'mIRC Script': '#3D57C3', 'mcfunction': '#E22837', - 'mupad': '#EDEDED', - 'nanorc': '#EDEDED', + 'mupad': '#244963', + 'nanorc': '#2D004D', 'nesC': '#94B0C7', 'ooc': '#B0B77E', 'q': '#0040CD', - 'reStructuredText': '#EDEDED', + 'reStructuredText': '#141414', + 'robots.txt': '#EDEDED', 'sed': '#64B970', 'wdl': '#42F1F4', 'wisp': '#7582D1', diff --git a/lib/src/const/token_env_keys.dart b/lib/src/const/token_env_keys.dart index 15d23a83..7a65804e 100644 --- a/lib/src/const/token_env_keys.dart +++ b/lib/src/const/token_env_keys.dart @@ -1,3 +1,4 @@ +// ignore: constant_identifier_names const List COMMON_GITHUB_TOKEN_ENV_KEYS = [ 'GITHUB_ADMIN_TOKEN', 'GITHUB_DART_TOKEN', diff --git a/lib/src/server/hooks.dart b/lib/src/server/hooks.dart index bb3c1018..ae3fa0bf 100644 --- a/lib/src/server/hooks.dart +++ b/lib/src/server/hooks.dart @@ -1,8 +1,11 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; + import 'package:json_annotation/json_annotation.dart'; + import '../common.dart'; +import '../common/model/changes.dart'; part 'hooks.g.dart'; @@ -30,7 +33,7 @@ class HookMiddleware { const Utf8Decoder().bind(request).join().then((content) { _eventController.add(HookEvent.fromJson( request.headers.value('X-GitHub-Event'), - jsonDecode(content) as Map)); + jsonDecode(content) as Map?)); request.response ..write(GitHubJson.encode({'handled': _eventController.hasListener})) ..close(); @@ -42,7 +45,7 @@ class HookServer extends HookMiddleware { final String host; final int port; - HttpServer _server; + late HttpServer _server; HookServer(this.port, [this.host = '0.0.0.0']); @@ -68,74 +71,112 @@ class HookServer extends HookMiddleware { class HookEvent { HookEvent(); - factory HookEvent.fromJson(String event, Map json) { + factory HookEvent.fromJson(String? event, Map? json) { if (event == 'pull_request') { - return PullRequestEvent.fromJson(json); + return PullRequestEvent.fromJson(json!); } else if (event == 'issues') { - return IssueEvent.fromJson(json); + return IssueEvent.fromJson(json!); } else if (event == 'issue_comment') { - return IssueCommentEvent.fromJson(json); + return IssueCommentEvent.fromJson(json!); } else if (event == 'repository') { - return RepositoryEvent.fromJson(json); + return RepositoryEvent.fromJson(json!); } return UnknownHookEvent(event, json); } } class UnknownHookEvent extends HookEvent { - final String event; - final Map data; + final String? event; + final Map? data; UnknownHookEvent(this.event, this.data); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() +class CheckRunEvent extends HookEvent { + CheckRunEvent({ + this.action, + this.checkRun, + this.sender, + this.repository, + }); + + factory CheckRunEvent.fromJson(Map input) => + _$CheckRunEventFromJson(input); + CheckRun? checkRun; + String? action; + User? sender; + Repository? repository; + + Map toJson() => _$CheckRunEventToJson(this); +} + +@JsonSerializable() +class CheckSuiteEvent extends HookEvent { + CheckSuiteEvent({ + this.action, + this.checkSuite, + this.repository, + this.sender, + }); + + String? action; + CheckSuite? checkSuite; + Repository? repository; + User? sender; + + factory CheckSuiteEvent.fromJson(Map input) => + _$CheckSuiteEventFromJson(input); + Map toJson() => _$CheckSuiteEventToJson(this); +} + +@JsonSerializable() class RepositoryEvent extends HookEvent { RepositoryEvent({ this.action, this.repository, this.sender, }); - String action; - Repository repository; - User sender; + String? action; + Repository? repository; + User? sender; factory RepositoryEvent.fromJson(Map input) => _$RepositoryEventFromJson(input); Map toJson() => _$RepositoryEventToJson(this); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class IssueCommentEvent extends HookEvent { IssueCommentEvent({ this.action, this.issue, this.comment, }); - String action; - Issue issue; - IssueComment comment; + String? action; + Issue? issue; + IssueComment? comment; factory IssueCommentEvent.fromJson(Map input) => _$IssueCommentEventFromJson(input); Map toJson() => _$IssueCommentEventToJson(this); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class ForkEvent extends HookEvent { ForkEvent({ this.forkee, this.sender, }); - Repository forkee; - User sender; + Repository? forkee; + User? sender; factory ForkEvent.fromJson(Map input) => _$ForkEventFromJson(input); Map toJson() => _$ForkEventToJson(this); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class IssueEvent extends HookEvent { IssueEvent({ this.action, @@ -145,19 +186,19 @@ class IssueEvent extends HookEvent { this.sender, this.repository, }); - String action; - User assignee; - IssueLabel label; - Issue issue; - User sender; - Repository repository; + String? action; + User? assignee; + IssueLabel? label; + Issue? issue; + User? sender; + Repository? repository; factory IssueEvent.fromJson(Map input) => _$IssueEventFromJson(input); Map toJson() => _$IssueEventToJson(this); } -@JsonSerializable(fieldRename: FieldRename.snake) +@JsonSerializable() class PullRequestEvent extends HookEvent { PullRequestEvent({ this.action, @@ -165,14 +206,37 @@ class PullRequestEvent extends HookEvent { this.pullRequest, this.sender, this.repository, + this.changes, }); - String action; - int number; - PullRequest pullRequest; - User sender; - Repository repository; + String? action; + int? number; + PullRequest? pullRequest; + User? sender; + Repository? repository; + Changes? changes; factory PullRequestEvent.fromJson(Map input) => _$PullRequestEventFromJson(input); Map toJson() => _$PullRequestEventToJson(this); } + +@JsonSerializable(fieldRename: FieldRename.snake) +class CreateEvent extends HookEvent { + CreateEvent({ + this.ref, + this.refType, + this.pusherType, + this.repository, + this.sender, + }); + + factory CreateEvent.fromJson(Map input) => + _$CreateEventFromJson(input); + String? ref; + String? refType; + String? pusherType; + Repository? repository; + User? sender; + + Map toJson() => _$CreateEventToJson(this); +} diff --git a/lib/src/server/hooks.g.dart b/lib/src/server/hooks.g.dart index 2c7dfcb5..81cbb790 100644 --- a/lib/src/server/hooks.g.dart +++ b/lib/src/server/hooks.g.dart @@ -6,17 +6,60 @@ part of 'hooks.dart'; // JsonSerializableGenerator // ************************************************************************** -RepositoryEvent _$RepositoryEventFromJson(Map json) { - return RepositoryEvent( - action: json['action'] as String, - repository: json['repository'] == null - ? null - : Repository.fromJson(json['repository'] as Map), - sender: json['sender'] == null - ? null - : User.fromJson(json['sender'] as Map), - ); -} +CheckRunEvent _$CheckRunEventFromJson(Map json) => + CheckRunEvent( + action: json['action'] as String?, + checkRun: json['check_run'] == null + ? null + : CheckRun.fromJson(json['check_run'] as Map), + sender: json['sender'] == null + ? null + : User.fromJson(json['sender'] as Map), + repository: json['repository'] == null + ? null + : Repository.fromJson(json['repository'] as Map), + ); + +Map _$CheckRunEventToJson(CheckRunEvent instance) => + { + 'check_run': instance.checkRun, + 'action': instance.action, + 'sender': instance.sender, + 'repository': instance.repository, + }; + +CheckSuiteEvent _$CheckSuiteEventFromJson(Map json) => + CheckSuiteEvent( + action: json['action'] as String?, + checkSuite: json['check_suite'] == null + ? null + : CheckSuite.fromJson(json['check_suite'] as Map), + repository: json['repository'] == null + ? null + : Repository.fromJson(json['repository'] as Map), + sender: json['sender'] == null + ? null + : User.fromJson(json['sender'] as Map), + ); + +Map _$CheckSuiteEventToJson(CheckSuiteEvent instance) => + { + 'action': instance.action, + 'check_suite': instance.checkSuite, + 'repository': instance.repository, + 'sender': instance.sender, + }; + +RepositoryEvent _$RepositoryEventFromJson(Map json) => + RepositoryEvent( + action: json['action'] as String?, + repository: json['repository'] == null + ? null + : Repository.fromJson(json['repository'] as Map), + sender: json['sender'] == null + ? null + : User.fromJson(json['sender'] as Map), + ); Map _$RepositoryEventToJson(RepositoryEvent instance) => { @@ -25,17 +68,16 @@ Map _$RepositoryEventToJson(RepositoryEvent instance) => 'sender': instance.sender, }; -IssueCommentEvent _$IssueCommentEventFromJson(Map json) { - return IssueCommentEvent( - action: json['action'] as String, - issue: json['issue'] == null - ? null - : Issue.fromJson(json['issue'] as Map), - comment: json['comment'] == null - ? null - : IssueComment.fromJson(json['comment'] as Map), - ); -} +IssueCommentEvent _$IssueCommentEventFromJson(Map json) => + IssueCommentEvent( + action: json['action'] as String?, + issue: json['issue'] == null + ? null + : Issue.fromJson(json['issue'] as Map), + comment: json['comment'] == null + ? null + : IssueComment.fromJson(json['comment'] as Map), + ); Map _$IssueCommentEventToJson(IssueCommentEvent instance) => { @@ -44,42 +86,38 @@ Map _$IssueCommentEventToJson(IssueCommentEvent instance) => 'comment': instance.comment, }; -ForkEvent _$ForkEventFromJson(Map json) { - return ForkEvent( - forkee: json['forkee'] == null - ? null - : Repository.fromJson(json['forkee'] as Map), - sender: json['sender'] == null - ? null - : User.fromJson(json['sender'] as Map), - ); -} +ForkEvent _$ForkEventFromJson(Map json) => ForkEvent( + forkee: json['forkee'] == null + ? null + : Repository.fromJson(json['forkee'] as Map), + sender: json['sender'] == null + ? null + : User.fromJson(json['sender'] as Map), + ); Map _$ForkEventToJson(ForkEvent instance) => { 'forkee': instance.forkee, 'sender': instance.sender, }; -IssueEvent _$IssueEventFromJson(Map json) { - return IssueEvent( - action: json['action'] as String, - assignee: json['assignee'] == null - ? null - : User.fromJson(json['assignee'] as Map), - label: json['label'] == null - ? null - : IssueLabel.fromJson(json['label'] as Map), - issue: json['issue'] == null - ? null - : Issue.fromJson(json['issue'] as Map), - sender: json['sender'] == null - ? null - : User.fromJson(json['sender'] as Map), - repository: json['repository'] == null - ? null - : Repository.fromJson(json['repository'] as Map), - ); -} +IssueEvent _$IssueEventFromJson(Map json) => IssueEvent( + action: json['action'] as String?, + assignee: json['assignee'] == null + ? null + : User.fromJson(json['assignee'] as Map), + label: json['label'] == null + ? null + : IssueLabel.fromJson(json['label'] as Map), + issue: json['issue'] == null + ? null + : Issue.fromJson(json['issue'] as Map), + sender: json['sender'] == null + ? null + : User.fromJson(json['sender'] as Map), + repository: json['repository'] == null + ? null + : Repository.fromJson(json['repository'] as Map), + ); Map _$IssueEventToJson(IssueEvent instance) => { @@ -91,21 +129,23 @@ Map _$IssueEventToJson(IssueEvent instance) => 'repository': instance.repository, }; -PullRequestEvent _$PullRequestEventFromJson(Map json) { - return PullRequestEvent( - action: json['action'] as String, - number: json['number'] as int, - pullRequest: json['pull_request'] == null - ? null - : PullRequest.fromJson(json['pull_request'] as Map), - sender: json['sender'] == null - ? null - : User.fromJson(json['sender'] as Map), - repository: json['repository'] == null - ? null - : Repository.fromJson(json['repository'] as Map), - ); -} +PullRequestEvent _$PullRequestEventFromJson(Map json) => + PullRequestEvent( + action: json['action'] as String?, + number: (json['number'] as num?)?.toInt(), + pullRequest: json['pull_request'] == null + ? null + : PullRequest.fromJson(json['pull_request'] as Map), + sender: json['sender'] == null + ? null + : User.fromJson(json['sender'] as Map), + repository: json['repository'] == null + ? null + : Repository.fromJson(json['repository'] as Map), + changes: json['changes'] == null + ? null + : Changes.fromJson(json['changes'] as Map), + ); Map _$PullRequestEventToJson(PullRequestEvent instance) => { @@ -114,4 +154,26 @@ Map _$PullRequestEventToJson(PullRequestEvent instance) => 'pull_request': instance.pullRequest, 'sender': instance.sender, 'repository': instance.repository, + 'changes': instance.changes, + }; + +CreateEvent _$CreateEventFromJson(Map json) => CreateEvent( + ref: json['ref'] as String?, + refType: json['ref_type'] as String?, + pusherType: json['pusher_type'] as String?, + repository: json['repository'] == null + ? null + : Repository.fromJson(json['repository'] as Map), + sender: json['sender'] == null + ? null + : User.fromJson(json['sender'] as Map), + ); + +Map _$CreateEventToJson(CreateEvent instance) => + { + 'ref': instance.ref, + 'ref_type': instance.refType, + 'pusher_type': instance.pusherType, + 'repository': instance.repository, + 'sender': instance.sender, }; diff --git a/lib/src/server/xplat_server.dart b/lib/src/server/xplat_server.dart index 3bd6e100..a6c85d02 100644 --- a/lib/src/server/xplat_server.dart +++ b/lib/src/server/xplat_server.dart @@ -27,5 +27,5 @@ Authentication findAuthenticationFromEnvironment() { } return findAuthenticationInMap(Platform.environment) ?? - Authentication.anonymous(); + const Authentication.anonymous(); } diff --git a/pubspec.yaml b/pubspec.yaml index 0ca8f0d0..eed1fec4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,23 +1,27 @@ name: github -version: 7.0.2 +version: 9.25.0 description: A high-level GitHub API Client Library that uses Github's v3 API homepage: https://github.com/SpinlockLabs/github.dart environment: - sdk: '>=2.3.0 <3.0.0' + sdk: ^3.5.0 dependencies: - http: '^0.12.0' - http_parser: ^3.1.1 - json_annotation: '>=2.0.0 <4.0.0' - meta: ^1.1.0 + http: ^1.0.0 + http_parser: ^4.0.0 + json_annotation: ^4.9.0 + meta: ^1.7.0 dev_dependencies: - build_runner: any - build_test: any - build_web_compilers: any - json_serializable: ^3.2.2 - mockito: ^3.0.0 - pedantic: ^1.0.0 - test: ^1.3.0 - yaml: ^2.2.0 + build_runner: ^2.2.1 + build_test: ^2.1.2 + build_web_compilers: '>=3.2.6 < 5.0.0' + collection: ^1.15.0 + dependency_validator: ^3.0.0 + json_serializable: ^6.6.1 + lints: ^5.0.0 + nock: ^1.1.3 + pub_semver: ^2.0.0 + test: ^1.21.6 + yaml: ^3.1.0 + yaml_edit: ^2.2.0 diff --git a/test/assets/responses/nocked_responses.dart b/test/assets/responses/nocked_responses.dart new file mode 100644 index 00000000..bf0e50f4 --- /dev/null +++ b/test/assets/responses/nocked_responses.dart @@ -0,0 +1,1480 @@ +String getBlob = ''' +{ + "content": "Q29udGVudCBvZiB0aGUgYmxvYg==", + "encoding": "base64", + "url": "https://api.github.com/repos/octocat/example/git/blobs/3a0f86fb8db8eea7ccbb9a95f325ddbedfb25e15", + "sha": "3a0f86fb8db8eea7ccbb9a95f325ddbedfb25e15", + "size": 19, + "node_id": "Q29udGVudCBvZiB0aGUgYmxvYg==" +}'''; + +String createBlob = ''' +{ + "url": "https://api.github.com/repos/octocat/example/git/blobs/3a0f86fb8db8eea7ccbb9a95f325ddbedfb25e15", + "sha": "3a0f86fb8db8eea7ccbb9a95f325ddbedfb25e15", + "content": "bbb", + "encoding": "utf-8" +}'''; + +String getCommit = ''' +{ + "sha": "7638417db6d59f3c431d3e1f261cc637155684cd", + "node_id": "MDY6Q29tbWl0NmRjYjA5YjViNTc4NzVmMzM0ZjYxYWViZWQ2OTVlMmU0MTkzZGI1ZQ==", + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", + "html_url": "https://github.com/octocat/Hello-World/commit/7638417db6d59f3c431d3e1f261cc637155684cd", + "author": { + "date": "2014-11-07T22:01:45Z", + "name": "Monalisa Octocat", + "email": "octocat@github.com" + }, + "committer": { + "date": "2014-11-07T22:01:45Z", + "name": "Monalisa Octocat", + "email": "octocat@github.com" + }, + "message": "added readme, because im a good github citizen", + "tree": { + "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/691272480426f78a0138979dd3ce63b77f706feb", + "sha": "691272480426f78a0138979dd3ce63b77f706feb" + }, + "parents": [ + { + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5", + "sha": "1acc419d4d6a9ce985db7be48c6349a0475975b5", + "html_url": "https://github.com/octocat/Hello-World/commit/7638417db6d59f3c431d3e1f261cc637155684cd" + } + ], + "verification": { + "verified": false, + "reason": "unsigned", + "signature": null, + "payload": null + } +}'''; + +String createCommit = ''' +{ + "sha": "7638417db6d59f3c431d3e1f261cc637155684cd", + "node_id": "MDY6Q29tbWl0NzYzODQxN2RiNmQ1OWYzYzQzMWQzZTFmMjYxY2M2MzcxNTU2ODRjZA==", + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", + "author": { + "date": "2014-11-07T22:01:45Z", + "name": "Monalisa Octocat", + "email": "octocat@github.com" + }, + "committer": { + "date": "2014-11-07T22:01:45Z", + "name": "Monalisa Octocat", + "email": "octocat@github.com" + }, + "message": "aMessage", + "tree": { + "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/827efc6d56897b048c772eb4087f854f46256132", + "sha": "aTreeSha" + }, + "parents": [ + { + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7d1b31e74ee336d15cbd21741bc88a537ed063a0", + "sha": "7d1b31e74ee336d15cbd21741bc88a537ed063a0", + "html_url": "https://github.com/octocat/Hello-World/commit/7d1b31e74ee336d15cbd21741bc88a537ed063a0" + } + ], + "verification": { + "verified": false, + "reason": "unsigned", + "signature": null, + "payload": null + }, + "html_url": "https://github.com/octocat/Hello-World/commit/7638417db6d59f3c431d3e1f261cc637155684cd" +}'''; + +String getReference = '''{ + "ref": "refs/heads/b", + "node_id": "MDM6UmVmcmVmcy9oZWFkcy9mZWF0dXJlQQ==", + "url": "https://api.github.com/repos/octocat/Hello-World/git/refs/heads/featureA", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } +}'''; + +String createReference = '''{ + "ref": "refs/heads/b", + "node_id": "MDM6UmVmcmVmcy9oZWFkcy9mZWF0dXJlQQ==", + "url": "https://api.github.com/repos/octocat/Hello-World/git/refs/heads/featureA", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } +}'''; + +String getTag = '''{ + "node_id": "MDM6VGFnOTQwYmQzMzYyNDhlZmFlMGY5ZWU1YmM3YjJkNWM5ODU4ODdiMTZhYw==", + "tag": "v0.0.1", + "sha": "940bd336248efae0f9ee5bc7b2d5c985887b16ac", + "url": "https://api.github.com/repos/octocat/Hello-World/git/tags/940bd336248efae0f9ee5bc7b2d5c985887b16ac", + "message": "initial version", + "tagger": { + "name": "Monalisa Octocat", + "email": "octocat@github.com", + "date": "2014-11-07T22:01:45Z" + }, + "object": { + "type": "commit", + "sha": "c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c", + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c" + }, + "verification": { + "verified": false, + "reason": "unsigned", + "signature": null, + "payload": null + } +}'''; + +String createTag = '''{ + "node_id": "MDM6VGFnOTQwYmQzMzYyNDhlZmFlMGY5ZWU1YmM3YjJkNWM5ODU4ODdiMTZhYw==", + "tag": "v0.0.1", + "sha": "940bd336248efae0f9ee5bc7b2d5c985887b16ac", + "url": "https://api.github.com/repos/octocat/Hello-World/git/tags/940bd336248efae0f9ee5bc7b2d5c985887b16ac", + "message": "initial version", + "tagger": { + "name": "Monalisa Octocat", + "email": "octocat@github.com", + "date": "2014-11-07T22:01:45Z" + }, + "object": { + "type": "commit", + "sha": "c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c", + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c" + }, + "verification": { + "verified": false, + "reason": "unsigned", + "signature": null, + "payload": null + } +}'''; + +String createTree = '''{ + "sha": "44b4fc6d56897b048c772eb4087f854f46256132", + "url": "https://api.github.com/repos/octocat/Hello-World/trees/44b4fc6d56897b048c772eb4087f854f46256132", + "tree": [ + { + "path": "file.rb", + "mode": "100644", + "type": "blob", + "size": 132, + "sha": "44b4fc6d56897b048c772eb4087f854f46256132", + "url": "https://api.github.com/repos/octocat/Hello-World/git/blobs/44b4fc6d56897b048c772eb4087f854f46256132" + } + ], + "truncated": true +}'''; + +String searchResults = '''{ + "total_count": 17, + "incomplete_results": false, + "items": [ + { + "name": "search.dart", + "path": "lib/src/common/model/search.dart", + "sha": "c61f39e54eeef0b20d132d2c3ea48bcd3b0d34a9", + "url": "https://api.github.com/repositories/22344823/contents/lib/src/common/model/search.dart?ref=27929ddc731393422327dddee0aaa56d0164c775", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/c61f39e54eeef0b20d132d2c3ea48bcd3b0d34a9", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/27929ddc731393422327dddee0aaa56d0164c775/lib/src/common/model/search.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "search_service.dart", + "path": "lib/src/common/search_service.dart", + "sha": "e98344a6f07d4d9ba1d5b1045354c6a8d3a5323f", + "url": "https://api.github.com/repositories/22344823/contents/lib/src/common/search_service.dart?ref=11a83b4fc9558b7f8c47fdced01c7a8dac3c65b2", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/e98344a6f07d4d9ba1d5b1045354c6a8d3a5323f", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/11a83b4fc9558b7f8c47fdced01c7a8dac3c65b2/lib/src/common/search_service.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "search.dart", + "path": "example/search.dart", + "sha": "9ca8b0ac2bfe0ce4afe2d49713ba639146f1ee1a", + "url": "https://api.github.com/repositories/22344823/contents/example/search.dart?ref=4875e4b34ade7f5e36443cd5a2716fe83d9360a2", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/9ca8b0ac2bfe0ce4afe2d49713ba639146f1ee1a", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/4875e4b34ade7f5e36443cd5a2716fe83d9360a2/example/search.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "emoji.dart", + "path": "example/emoji.dart", + "sha": "7604d4619400b6b2a19ab11baa60dfa6fa08843e", + "url": "https://api.github.com/repositories/22344823/contents/example/emoji.dart?ref=4875e4b34ade7f5e36443cd5a2716fe83d9360a2", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/7604d4619400b6b2a19ab11baa60dfa6fa08843e", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/4875e4b34ade7f5e36443cd5a2716fe83d9360a2/example/emoji.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "search.html", + "path": "example/search.html", + "sha": "16f41b72e4e6135c63aaf923be50a6c87ec80126", + "url": "https://api.github.com/repositories/22344823/contents/example/search.html?ref=4875e4b34ade7f5e36443cd5a2716fe83d9360a2", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/16f41b72e4e6135c63aaf923be50a6c87ec80126", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/4875e4b34ade7f5e36443cd5a2716fe83d9360a2/example/search.html", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "mocks.mocks.dart", + "path": "test/src/mocks.mocks.dart", + "sha": "c381f58a8641b8814afd65b6b7a39384035e2ae3", + "url": "https://api.github.com/repositories/22344823/contents/test/src/mocks.mocks.dart?ref=7056f9c2bf17c5f437e6cb32012a7d16f9ed3278", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/c381f58a8641b8814afd65b6b7a39384035e2ae3", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/7056f9c2bf17c5f437e6cb32012a7d16f9ed3278/test/src/mocks.mocks.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "emoji.html", + "path": "example/emoji.html", + "sha": "bdafb143dd918a4872e982b3f876c32aaf9877b2", + "url": "https://api.github.com/repositories/22344823/contents/example/emoji.html?ref=4875e4b34ade7f5e36443cd5a2716fe83d9360a2", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/bdafb143dd918a4872e982b3f876c32aaf9877b2", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/4875e4b34ade7f5e36443cd5a2716fe83d9360a2/example/emoji.html", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "index.html", + "path": "example/index.html", + "sha": "81e054e8a613cb2932f905e42c3dc2e294e551ac", + "url": "https://api.github.com/repositories/22344823/contents/example/index.html?ref=c72b46031fcd326820cbc5bb0c3b4b1c15e075e4", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/81e054e8a613cb2932f905e42c3dc2e294e551ac", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/c72b46031fcd326820cbc5bb0c3b4b1c15e075e4/example/index.html", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "readme.md", + "path": "example/readme.md", + "sha": "0781c2ba3898ee16123d9b63a7685d588f3f7511", + "url": "https://api.github.com/repositories/22344823/contents/example/readme.md?ref=4875e4b34ade7f5e36443cd5a2716fe83d9360a2", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/0781c2ba3898ee16123d9b63a7685d588f3f7511", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/4875e4b34ade7f5e36443cd5a2716fe83d9360a2/example/readme.md", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "CHANGELOG.md", + "path": "CHANGELOG.md", + "sha": "5de4e987d591c1f71b8f94311671fa8edb38ca6b", + "url": "https://api.github.com/repositories/22344823/contents/CHANGELOG.md?ref=f90446459e2723baecc16f8ac725b5147bd915ab", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/5de4e987d591c1f71b8f94311671fa8edb38ca6b", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/f90446459e2723baecc16f8ac725b5147bd915ab/CHANGELOG.md", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "github.dart", + "path": "lib/src/common/github.dart", + "sha": "e715c2a9b4439c24f00b00071cc86db63c426f1e", + "url": "https://api.github.com/repositories/22344823/contents/lib/src/common/github.dart?ref=921269ba8f803ba47470c624460c23c289b3291b", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/e715c2a9b4439c24f00b00071cc86db63c426f1e", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/921269ba8f803ba47470c624460c23c289b3291b/lib/src/common/github.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "common.dart", + "path": "lib/src/common.dart", + "sha": "f8be345ced35b7320355e04663f7504cb0a502b6", + "url": "https://api.github.com/repositories/22344823/contents/lib/src/common.dart?ref=4875e4b34ade7f5e36443cd5a2716fe83d9360a2", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/f8be345ced35b7320355e04663f7504cb0a502b6", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/4875e4b34ade7f5e36443cd5a2716fe83d9360a2/lib/src/common.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "search.dart", + "path": "test/experiment/search.dart", + "sha": "1fffe4dd40ca49cff3f96d248a1740ac67e6a602", + "url": "https://api.github.com/repositories/22344823/contents/test/experiment/search.dart?ref=4875e4b34ade7f5e36443cd5a2716fe83d9360a2", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/1fffe4dd40ca49cff3f96d248a1740ac67e6a602", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/4875e4b34ade7f5e36443cd5a2716fe83d9360a2/test/experiment/search.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "code_search_test.dart", + "path": "test/code_search_test.dart", + "sha": "3d60cf6329a298d99d8b074009c2a8fd3c39a470", + "url": "https://api.github.com/repositories/22344823/contents/test/code_search_test.dart?ref=4875e4b34ade7f5e36443cd5a2716fe83d9360a2", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/3d60cf6329a298d99d8b074009c2a8fd3c39a470", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/4875e4b34ade7f5e36443cd5a2716fe83d9360a2/test/code_search_test.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "xplat_browser.dart", + "path": "lib/src/browser/xplat_browser.dart", + "sha": "79aeeb174148ae38f6a26d2297356b160e504b6b", + "url": "https://api.github.com/repositories/22344823/contents/lib/src/browser/xplat_browser.dart?ref=4875e4b34ade7f5e36443cd5a2716fe83d9360a2", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/79aeeb174148ae38f6a26d2297356b160e504b6b", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/4875e4b34ade7f5e36443cd5a2716fe83d9360a2/lib/src/browser/xplat_browser.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "release_notes.dart", + "path": "example/release_notes.dart", + "sha": "867474ee071b0b026fcf64cd2409830279c8b2db", + "url": "https://api.github.com/repositories/22344823/contents/example/release_notes.dart?ref=921269ba8f803ba47470c624460c23c289b3291b", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/867474ee071b0b026fcf64cd2409830279c8b2db", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/921269ba8f803ba47470c624460c23c289b3291b/example/release_notes.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + }, + { + "name": "release_unreleased_prs.dart", + "path": "tool/release_unreleased_prs.dart", + "sha": "aca8dae11ec0a26804761ea302934829bf70b4c9", + "url": "https://api.github.com/repositories/22344823/contents/tool/release_unreleased_prs.dart?ref=7056f9c2bf17c5f437e6cb32012a7d16f9ed3278", + "git_url": "https://api.github.com/repositories/22344823/git/blobs/aca8dae11ec0a26804761ea302934829bf70b4c9", + "html_url": "https://github.com/SpinlockLabs/github.dart/blob/7056f9c2bf17c5f437e6cb32012a7d16f9ed3278/tool/release_unreleased_prs.dart", + "repository": { + "id": 22344823, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjM0NDgyMw==", + "name": "github.dart", + "full_name": "SpinlockLabs/github.dart", + "private": false, + "owner": { + "login": "SpinlockLabs", + "id": 26679435, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI2Njc5NDM1", + "avatar_url": "https://avatars.githubusercontent.com/u/26679435?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SpinlockLabs", + "html_url": "https://github.com/SpinlockLabs", + "followers_url": "https://api.github.com/users/SpinlockLabs/followers", + "following_url": "https://api.github.com/users/SpinlockLabs/following{/other_user}", + "gists_url": "https://api.github.com/users/SpinlockLabs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SpinlockLabs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SpinlockLabs/subscriptions", + "organizations_url": "https://api.github.com/users/SpinlockLabs/orgs", + "repos_url": "https://api.github.com/users/SpinlockLabs/repos", + "events_url": "https://api.github.com/users/SpinlockLabs/events{/privacy}", + "received_events_url": "https://api.github.com/users/SpinlockLabs/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/SpinlockLabs/github.dart", + "description": "GitHub Client Library for Dart", + "fork": false, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart", + "forks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/forks", + "keys_url": "https://api.github.com/repos/SpinlockLabs/github.dart/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/SpinlockLabs/github.dart/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/SpinlockLabs/github.dart/teams", + "hooks_url": "https://api.github.com/repos/SpinlockLabs/github.dart/hooks", + "issue_events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/events{/number}", + "events_url": "https://api.github.com/repos/SpinlockLabs/github.dart/events", + "assignees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/assignees{/user}", + "branches_url": "https://api.github.com/repos/SpinlockLabs/github.dart/branches{/branch}", + "tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/tags", + "blobs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/SpinlockLabs/github.dart/statuses/{sha}", + "languages_url": "https://api.github.com/repos/SpinlockLabs/github.dart/languages", + "stargazers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/stargazers", + "contributors_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contributors", + "subscribers_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscribers", + "subscription_url": "https://api.github.com/repos/SpinlockLabs/github.dart/subscription", + "commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/SpinlockLabs/github.dart/contents/{+path}", + "compare_url": "https://api.github.com/repos/SpinlockLabs/github.dart/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/SpinlockLabs/github.dart/merges", + "archive_url": "https://api.github.com/repos/SpinlockLabs/github.dart/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/SpinlockLabs/github.dart/downloads", + "issues_url": "https://api.github.com/repos/SpinlockLabs/github.dart/issues{/number}", + "pulls_url": "https://api.github.com/repos/SpinlockLabs/github.dart/pulls{/number}", + "milestones_url": "https://api.github.com/repos/SpinlockLabs/github.dart/milestones{/number}", + "notifications_url": "https://api.github.com/repos/SpinlockLabs/github.dart/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/SpinlockLabs/github.dart/labels{/name}", + "releases_url": "https://api.github.com/repos/SpinlockLabs/github.dart/releases{/id}", + "deployments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/deployments" + }, + "score": 1.0 + } + ] +}'''; + +String mergedPR1 = '''{ + "sha": "someSHA", + "merged": true, + "message": "Pull Request successfully merged" +}'''; diff --git a/test/code_search_test.dart b/test/code_search_test.dart deleted file mode 100644 index 06ade1aa..00000000 --- a/test/code_search_test.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'dart:io'; -import 'package:github/github.dart'; - -Future main() async { - print('Searching ...'); - final github = GitHub(); - - final resultsStream = github.search.code( - 'github', - repo: 'SpinlockLabs/github.dart', - perPage: 5, - pages: 1, - ); - final results = await resultsStream.first; - print('${results.totalCount} results'); - var k = 1; - for (final i in results.items) { - print('${k++} ${i.path}'); - } - exit(0); -} diff --git a/test/common/data/repos_json.dart b/test/common/data/repos_json.dart new file mode 100644 index 00000000..f5087cf7 --- /dev/null +++ b/test/common/data/repos_json.dart @@ -0,0 +1,81 @@ +const String listCommits = ''' +[ + { + "sha": "3771b3c9ab1912d32a0d0baffaf489d561caf558", + "node_id": "C_kwDOAVT0d9oAKDM3NzFiM2M5YWIxOTEyZDMyYTBkMGJhZmZhZjQ4OWQ1NjFjYWY1NTg", + "commit": { + "author": { + "name": "Rob Becker", + "email": "rob.becker@workiva.com", + "date": "2023-04-17T14:55:14Z" + }, + "committer": { + "name": "Rob Becker", + "email": "rob.becker@workiva.com", + "date": "2023-04-17T14:55:14Z" + }, + "message": "prep 9.12.0", + "tree": { + "sha": "282532b41e8fead81ec6d68e7e603139e7dd7581", + "url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/trees/282532b41e8fead81ec6d68e7e603139e7dd7581" + }, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart/git/commits/3771b3c9ab1912d32a0d0baffaf489d561caf558", + "comment_count": 0, + "verification": { + "verified": true, + "reason": "valid" + } + }, + "url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits/3771b3c9ab1912d32a0d0baffaf489d561caf558", + "html_url": "https://github.com/SpinlockLabs/github.dart/commit/3771b3c9ab1912d32a0d0baffaf489d561caf558", + "comments_url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits/3771b3c9ab1912d32a0d0baffaf489d561caf558/comments", + "author": { + "login": "robbecker-wf", + "id": 6053699, + "node_id": "MDQ6VXNlcjYwNTM2OTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/6053699?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/robbecker-wf", + "html_url": "https://github.com/robbecker-wf", + "followers_url": "https://api.github.com/users/robbecker-wf/followers", + "following_url": "https://api.github.com/users/robbecker-wf/following{/other_user}", + "gists_url": "https://api.github.com/users/robbecker-wf/gists{/gist_id}", + "starred_url": "https://api.github.com/users/robbecker-wf/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/robbecker-wf/subscriptions", + "organizations_url": "https://api.github.com/users/robbecker-wf/orgs", + "repos_url": "https://api.github.com/users/robbecker-wf/repos", + "events_url": "https://api.github.com/users/robbecker-wf/events{/privacy}", + "received_events_url": "https://api.github.com/users/robbecker-wf/received_events", + "type": "User", + "site_admin": false + }, + "committer": { + "login": "robbecker-wf", + "id": 6053699, + "node_id": "MDQ6VXNlcjYwNTM2OTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/6053699?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/robbecker-wf", + "html_url": "https://github.com/robbecker-wf", + "followers_url": "https://api.github.com/users/robbecker-wf/followers", + "following_url": "https://api.github.com/users/robbecker-wf/following{/other_user}", + "gists_url": "https://api.github.com/users/robbecker-wf/gists{/gist_id}", + "starred_url": "https://api.github.com/users/robbecker-wf/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/robbecker-wf/subscriptions", + "organizations_url": "https://api.github.com/users/robbecker-wf/orgs", + "repos_url": "https://api.github.com/users/robbecker-wf/repos", + "events_url": "https://api.github.com/users/robbecker-wf/events{/privacy}", + "received_events_url": "https://api.github.com/users/robbecker-wf/received_events", + "type": "User", + "site_admin": false + }, + "parents": [ + { + "sha": "a3081681da68383d9a5f18ef3502f47f7144e7d8", + "url": "https://api.github.com/repos/SpinlockLabs/github.dart/commits/a3081681da68383d9a5f18ef3502f47f7144e7d8", + "html_url": "https://github.com/SpinlockLabs/github.dart/commit/a3081681da68383d9a5f18ef3502f47f7144e7d8" + } + ] + } +] +'''; diff --git a/test/common/github_test.dart b/test/common/github_test.dart new file mode 100644 index 00000000..97ce2930 --- /dev/null +++ b/test/common/github_test.dart @@ -0,0 +1,59 @@ +import 'dart:io'; + +import 'package:github/src/common.dart'; +import 'package:http/http.dart'; +import 'package:http/testing.dart'; +import 'package:test/test.dart'; + +void main() { + group(GitHub, () { + test('passes calendar version header', () async { + Request? request; + final client = MockClient((r) async { + request = r; + return Response('{}', HttpStatus.ok); + }); + + final github = GitHub(client: client); + await github.getJSON(''); // Make HTTP request + + expect(request, isNotNull); + expect(request!.headers.containsKey(GitHub.versionHeader), isTrue); + final version = request!.headers[GitHub.versionHeader]; + expect(version, github.version); + }); + + test('passes required user-agent header', () async { + Request? request; + final client = MockClient((r) async { + request = r; + return Response('{}', HttpStatus.ok); + }); + + final github = GitHub(client: client); + await github.getJSON(''); // Make HTTP request + + expect(request, isNotNull); + expect(request!.headers.containsKey('User-Agent'), isTrue); + final userAgent = request!.headers['User-Agent']; + expect(userAgent, 'github.dart'); + }); + + test('anonymous auth passes no authorization header', () async { + Request? request; + final client = MockClient((r) async { + request = r; + return Response('{}', HttpStatus.ok); + }); + + final github = GitHub( + client: client, + auth: const Authentication.anonymous(), + ); + await github.getJSON(''); // Make HTTP request + + expect(request, isNotNull); + expect(request!.headers.containsKey('Authorization'), isFalse); + }); + }); +} diff --git a/test/common/misc_service_test.dart b/test/common/misc_service_test.dart new file mode 100644 index 00000000..2066e271 --- /dev/null +++ b/test/common/misc_service_test.dart @@ -0,0 +1,37 @@ +import 'dart:io'; + +import 'package:github/src/common.dart'; +import 'package:http/http.dart'; +import 'package:http/testing.dart'; +import 'package:test/test.dart'; + +void main() { + MiscService create(Future Function(Request) f) { + final client = MockClient(f); + final github = GitHub(client: client); + return MiscService(github); + } + + test('api status', () async { + final miscService = create( + (_) async => Response(''' +{ + "page":{ + "id":"kctbh9vrtdwd", + "name":"GitHub", + "url":"https://www.githubstatus.com", + "updated_at": "2023-11-29T08:03:04Z" + }, + "status": { + "description": "Partial System Outage", + "indicator": "major" + } +}''', HttpStatus.ok), + ); + final status = await miscService.getApiStatus(); + expect(status.page, isNotNull); + expect(status.page?.id, 'kctbh9vrtdwd'); + expect(status.status, isNotNull); + expect(status.status?.indicator, 'major'); + }); +} diff --git a/test/common/repos_service_test.dart b/test/common/repos_service_test.dart new file mode 100644 index 00000000..2f3f9120 --- /dev/null +++ b/test/common/repos_service_test.dart @@ -0,0 +1,49 @@ +import 'package:github/src/common.dart'; +import 'package:http/http.dart'; +import 'package:http/testing.dart'; +import 'package:test/test.dart'; + +import 'data/repos_json.dart'; + +void main() { + final slug = RepositorySlug('SpinlockLabs', 'github.dart'); + RepositoriesService create(Future Function(Request) f) { + final client = MockClient(f); + final github = GitHub(client: client); + return RepositoriesService(github); + } + + test('listCommits', () async { + final repositories = create((request) async { + expect(request.url.path, '/repos/${slug.fullName}/commits'); + expect(request.url.query, isEmpty); + + return Response(listCommits, StatusCodes.OK); + }); + final commits = await repositories.listCommits(slug).toList(); + expect(commits, hasLength(1)); + }); + + test('listCommits with query params', () async { + final repositories = create((request) async { + expect(request.url.path, '/repos/${slug.fullName}/commits'); + expect( + request.url.query, + 'author=octocat&committer=octodog&sha=abc&path=%2Fpath&since=2022-02-22T00%3A00%3A00.000&until=2023-02-22T00%3A00%3A00.000', + ); + return Response(listCommits, StatusCodes.OK); + }); + final commits = await repositories + .listCommits( + slug, + sha: 'abc', + path: '/path', + author: 'octocat', + committer: 'octodog', + since: DateTime(2022, 2, 22), + until: DateTime(2023, 2, 22), + ) + .toList(); + expect(commits, hasLength(1)); + }); +} diff --git a/test/data_object_test.dart b/test/data_object_test.dart index 2f2012a7..f1d1d5db 100644 --- a/test/data_object_test.dart +++ b/test/data_object_test.dart @@ -1,7 +1,7 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:github/github.dart'; +import 'package:test/test.dart'; const _licenseJson = r''' { "name": "LICENSE", diff --git a/test/experiment/crawler.dart b/test/experiment/crawler.dart index cc4e23ee..7dd8e737 100644 --- a/test/experiment/crawler.dart +++ b/test/experiment/crawler.dart @@ -1,7 +1,7 @@ import 'package:github/github.dart'; void main() { - final github = GitHub(auth: Authentication.anonymous()); + final github = GitHub(auth: const Authentication.anonymous()); final crawler = RepositoryCrawler( github, diff --git a/test/experiment/error_handling.dart b/test/experiment/error_handling.dart index 5456b237..e76b7e9d 100644 --- a/test/experiment/error_handling.dart +++ b/test/experiment/error_handling.dart @@ -27,8 +27,4 @@ void main() { print(e); exit(0); } - - print('Invalid Entity Error Handling Failed'); - - exit(1); } diff --git a/test/experiment/files.dart b/test/experiment/files.dart index 69d17e6f..37efa7b0 100755 --- a/test/experiment/files.dart +++ b/test/experiment/files.dart @@ -9,6 +9,6 @@ void main() { 'pubspec.yaml', ) .then((contents) => contents.file) - .then((file) => print(file.text)) + .then((file) => print(file?.text)) .then((_) => github.dispose()); } diff --git a/test/experiment/generate_release_notes.dart b/test/experiment/generate_release_notes.dart new file mode 100755 index 00000000..8d0e7a1e --- /dev/null +++ b/test/experiment/generate_release_notes.dart @@ -0,0 +1,11 @@ +import 'package:github/github.dart'; + +Future main() async { + final github = GitHub(auth: findAuthenticationFromEnvironment()); + + var notes = await github.repositories.generateReleaseNotes(CreateReleaseNotes( + 'Spinlocklabs', 'github.dart', '1.0.1', + targetCommitish: '1.0.1', previousTagName: '1.0.0')); + print(notes.name); + print(notes.body); +} diff --git a/test/experiment/limit_pager.dart b/test/experiment/limit_pager.dart index c8b14739..05654bfa 100755 --- a/test/experiment/limit_pager.dart +++ b/test/experiment/limit_pager.dart @@ -5,8 +5,8 @@ void main() { print(solve(201)); } -const int MAX_PER_PAGE = 100; -const int ACCURACY_RANGE = 5; +const int maxPerPage = 100; +const int accuracyRange = 5; /// Solves the most efficient way to fetch the number of objects [limit] with the least requests. PaginationInformation solve(int limit) { @@ -14,12 +14,12 @@ PaginationInformation solve(int limit) { throw RangeError('limit cannot be less than zero (was $limit)'); } - if (limit < MAX_PER_PAGE) { + if (limit < maxPerPage) { return PaginationInformation(limit, 1, limit); } - if ((limit % MAX_PER_PAGE) == 0) { - return PaginationInformation(limit, limit ~/ MAX_PER_PAGE, MAX_PER_PAGE); + if ((limit % maxPerPage) == 0) { + return PaginationInformation(limit, limit ~/ maxPerPage, maxPerPage); } const itemsPerPage = 100; diff --git a/test/experiment/reactions.dart b/test/experiment/reactions.dart index 0b07d915..76f2a8b4 100644 --- a/test/experiment/reactions.dart +++ b/test/experiment/reactions.dart @@ -6,6 +6,6 @@ void main() { .listReactions(RepositorySlug('SpinlockLabs', 'github.dart'), 177, content: ReactionType.plusOne) .listen((Reaction r) { - print(ReactionType.fromString(r.content).emoji); + print(ReactionType.fromString(r.content)!.emoji); }); } diff --git a/test/experiment/search.dart b/test/experiment/search.dart index 1aacb5c1..1fffe4dd 100755 --- a/test/experiment/search.dart +++ b/test/experiment/search.dart @@ -4,7 +4,6 @@ void main() { final github = GitHub(); github.search.repositories('github').listen((repo) { - print( - "${repo.fullName}: ${repo.description.isNotEmpty ? repo.description : "No Description"}"); + print('${repo.fullName}: ${repo.description}'); }).onDone(github.dispose); } diff --git a/test/git_test.dart b/test/git_test.dart index 813f5946..e95ef5d6 100644 --- a/test/git_test.dart +++ b/test/git_test.dart @@ -1,305 +1,263 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:github/src/common.dart'; -import 'package:http/http.dart' as http; -import 'package:mockito/mockito.dart'; +import 'package:github/github.dart'; +import 'package:nock/nock.dart'; import 'package:test/test.dart'; -class MockGitHub extends Mock implements GitHub {} +import 'assets/responses/nocked_responses.dart' as nocked; + +const fakeApiUrl = 'http://fake.api.github.com'; +const date = '2014-10-02T15:21:29Z'; + +GitHub createGithub() { + return GitHub( + endpoint: fakeApiUrl, + auth: const Authentication.withToken( + '0000000000000000000000000000000000000001')); +} void main() { - MockGitHub github; - GitService git; - RepositorySlug repo; + late GitHub github; + late GitService git; + late RepositorySlug repo; const someSha = 'someSHA'; + setUpAll(nock.init); + setUp(() { - github = MockGitHub(); + nock.cleanAll(); + github = createGithub(); git = GitService(github); repo = RepositorySlug('o', 'n'); }); + tearDown(nock.cleanAll); - group('getBlob()', () { - test('constructs correct path', () { - git.getBlob(repo, 'sh'); - - verify(github.getJSON('/repos/o/n/git/blobs/sh', - convert: (i) => GitBlob.fromJson(i), statusCode: StatusCodes.OK)); - }); + test('getBlob()', () async { + nock(fakeApiUrl).get('/repos/o/n/git/blobs/sh').reply(200, nocked.getBlob); + final blob = await git.getBlob(repo, 'sh'); + expect(blob.sha, '3a0f86fb8db8eea7ccbb9a95f325ddbedfb25e15'); + expect(nock.pendingMocks.isEmpty, true); }); - group('createBlob()', () { - test('constructs correct path', () { - var blob = CreateGitBlob('bbb', 'utf-8'); - git.createBlob(repo, blob); - - verify(github.postJSON( - '/repos/o/n/git/blobs', - convert: (i) => GitBlob.fromJson(i), - statusCode: StatusCodes.CREATED, - body: GitHubJson.encode(blob), - )); - }); - - test('creates valid JSON body', () { - var blob = CreateGitBlob('bbb', 'utf-8'); - - git.createBlob(repo, blob); - final body = captureSentBody(github); - expect(body['content'], equals('bbb')); - expect(body['encoding'], equals('utf-8')); - }); + test('createBlob()', () async { + nock(fakeApiUrl) + .post('/repos/o/n/git/blobs', '{"content":"bbb","encoding":"utf-8"}') + .reply(201, nocked.createBlob); + var blob = await git.createBlob(repo, CreateGitBlob('bbb', 'utf-8')); + expect(blob.content, 'bbb'); + expect(blob.encoding, 'utf-8'); }); - group('getCommit()', () { - test('constructs correct path', () { - git.getCommit(repo, 'sh'); - - verify(github.getJSON('/repos/o/n/git/commits/sh', - convert: (i) => GitCommit.fromJson(i), statusCode: StatusCodes.OK)); - }); + test('getCommit()', () async { + nock(fakeApiUrl) + .get('/repos/o/n/git/commits/sh') + .reply(200, nocked.getCommit); + var commit = await git.getCommit(repo, 'sh'); + expect(commit.sha, '7638417db6d59f3c431d3e1f261cc637155684cd'); }); - group('createCommit()', () { - test('constructs correct path', () { - final commit = CreateGitCommit('aMessage', 'aTreeSha'); - git.createCommit(repo, commit); - - verify(github.postJSON( - '/repos/o/n/git/commits', - convert: (i) => GitCommit.fromJson(i), - statusCode: StatusCodes.CREATED, - body: GitHubJson.encode(commit), - )); - }); - - test('creates valid JSON body', () { - // given - const date = '2014-10-02T15:21:29Z'; - - final commit = CreateGitCommit('aMessage', 'aTreeSha') - ..parents = ['parentSha1', 'parentSha2'] - ..committer = GitCommitUser('cName', 'cEmail', parseDateTime(date)) - ..author = GitCommitUser('aName', 'aEmail', parseDateTime(date)); - - // when - git.createCommit(repo, commit); - - // then - final body = captureSentBody(github); - expect(body['message'], equals('aMessage')); - expect(body['tree'], equals('aTreeSha')); - expect(body['parents'], equals(['parentSha1', 'parentSha2'])); - expect(body['committer']['name'], equals('cName')); - expect(body['committer']['email'], equals('cEmail')); - expect(body['committer']['date'], equals(date)); - expect(body['author']['name'], equals('aName')); - expect(body['author']['email'], equals('aEmail')); - expect(body['author']['date'], equals(date)); - }); + test('createCommit()', () async { + nock(fakeApiUrl) + .post('/repos/o/n/git/commits', + '{"message":"aMessage","tree":"aTreeSha","parents":["parentSha1","parentSha2"],"committer":{"name":"cName","email":"cEmail","date":"2014-10-02T15:21:29Z"},"author":{"name":"aName","email":"aEmail","date":"2014-10-02T15:21:29Z"}}') + .reply(201, nocked.createCommit); + + var commit = await git.createCommit( + repo, + CreateGitCommit('aMessage', 'aTreeSha') + ..parents = ['parentSha1', 'parentSha2'] + ..committer = GitCommitUser('cName', 'cEmail', DateTime.parse(date)) + ..author = GitCommitUser('aName', 'aEmail', DateTime.parse(date))); + expect(commit.message, 'aMessage'); + expect(commit.tree!.sha, 'aTreeSha'); }); - group('getReference()', () { - test('constructs correct path', () { - git.getReference(repo, 'heads/b'); - - verify(github.getJSON('/repos/o/n/git/refs/heads/b', - convert: (i) => GitReference.fromJson(i), - statusCode: StatusCodes.OK)); - }); + test('getReference()', () async { + nock(fakeApiUrl) + .get('/repos/o/n/git/refs/heads/b') + .reply(200, nocked.getReference); + var ref = await git.getReference(repo, 'heads/b'); + expect(ref.ref, 'refs/heads/b'); }); - group('createReference()', () { + test('createReference()', () async { const someRef = 'refs/heads/b'; - test('constructs correct path', () { - git.createReference(repo, someRef, someSha); - - verify(github.postJSON('/repos/o/n/git/refs', - convert: (i) => GitReference.fromJson(i), - statusCode: StatusCodes.CREATED, - body: GitHubJson.encode({'ref': someRef, 'sha': someSha}))); - }); - - test('creates valid JSON body', () { - git.createReference(repo, someRef, someSha); - - final body = captureSentBody(github); - expect(body['ref'], equals(someRef)); - expect(body['sha'], equals(someSha)); - }); + nock(fakeApiUrl) + .post('/repos/o/n/git/refs', '{"ref":"refs/heads/b","sha":"someSHA"}') + .reply(201, nocked.createReference); + var ref = await git.createReference(repo, someRef, someSha); + expect(ref.ref, someRef); }); - group('editReference()', () { - test('constructs correct path', () { - // given - final expectedResponse = http.Response('{}', 200); + test('editReference()', () async { + nock(fakeApiUrl) + .patch('/repos/o/n/git/refs/heads/b', '{"sha":"someSHA","force":true}') + .reply(200, '{}'); - when(github.request(any, any, body: any, headers: any)) - .thenReturn(Future.value(expectedResponse)); - - // when - git.editReference(repo, 'heads/b', someSha); - - // then - verify(github.request('PATCH', '/repos/o/n/git/refs/heads/b', - headers: any, body: any)); - }); + await git.editReference(repo, 'heads/b', someSha, force: true); + }); - test('creates valid JSON body', () { - // given - final expectedResponse = http.Response('{}', 200); - when(github.request(any, any, body: any, headers: any)) - .thenReturn(Future.value(expectedResponse)); - - // when - git.editReference(repo, 'heads/b', someSha, force: true); - - // then - final captured = verify(github.request( - any, - any, - body: captureAny, - headers: captureAny, - )).captured; - - final body = jsonDecode(captured[0]); - final headers = captured[1]; - - expect(body['sha'], equals(someSha)); - expect(body['force'], equals(true)); - expect(headers['content-length'], equals('30')); - }); + test('deleteReference()', () async { + nock(fakeApiUrl).delete('/repos/o/n/git/refs/heads/b').reply(200, '{}'); + await git.deleteReference(repo, 'heads/b'); }); - group('deleteReference()', () { - test('constructs correct path', () { - // given - final expectedResponse = http.Response('{}', 200); - when(github.request(any, any)).thenReturn(Future.value(expectedResponse)); + test('getTag()', () async { + nock(fakeApiUrl) + .get('/repos/o/n/git/tags/someSHA') + .reply(200, nocked.getTag); + await git.getTag(repo, someSha); + }); - // when - git.deleteReference(repo, 'heads/b'); + test('createTag()', () async { + nock(fakeApiUrl) + .post('/repos/o/n/git/tags', + '{"tag":"v0.0.1","message":"initial version","object":"someSHA","type":"commit","tagger":{"name":"Monalisa Octocat","email":"octocat@github.com","date":"$date"}}') + .reply(201, nocked.createTag); + + final createGitTag = CreateGitTag( + 'v0.0.1', + 'initial version', + someSha, + 'commit', + GitCommitUser( + 'Monalisa Octocat', 'octocat@github.com', DateTime.parse(date))); + + var tag = await git.createTag(repo, createGitTag); + + expect(tag.tag, 'v0.0.1'); + expect(tag.message, 'initial version'); + expect(tag.tagger?.name, 'Monalisa Octocat'); + }); - // then - verify(github.request('DELETE', '/repos/o/n/git/refs/heads/b')); - }); + test('getTree()', () async { + nock(fakeApiUrl) + .get('/repos/o/n/git/trees/sh?recursive=1') + .reply(200, '{}'); + await git.getTree(repo, 'sh', recursive: true); }); - group('getTag()', () { - test('constructs correct path', () { - git.getTag(repo, someSha); + test('createTree()', () async { + nock(fakeApiUrl) + .post('/repos/o/n/git/trees', + '{"tree":[{"path":"file.rb","mode":"100644","type":"blob","sha":"44b4fc6d56897b048c772eb4087f854f46256132"}]}') + .reply(201, nocked.createTree); + + var createTree = CreateGitTree([ + CreateGitTreeEntry('file.rb', '100644', 'blob', + sha: '44b4fc6d56897b048c772eb4087f854f46256132') + ]); + + var tree = await git.createTree(repo, createTree); + var entry = tree.entries?.first; + expect(entry?.path, 'file.rb'); + expect(entry?.mode, '100644'); + expect(entry?.type, 'blob'); + expect(entry?.sha, '44b4fc6d56897b048c772eb4087f854f46256132'); + + nock(fakeApiUrl) + .post('/repos/o/n/git/trees', + '{"tree":[{"path":"file.rb","mode":"100644","type":"blob","content":"content"}]}') + .reply(201, nocked.createTree); + + createTree = CreateGitTree( + [CreateGitTreeEntry('file.rb', '100644', 'blob', content: 'content')]); + + tree = await git.createTree(repo, createTree); + entry = tree.entries?.first; + expect(entry?.path, 'file.rb'); + expect(entry?.mode, '100644'); + expect(entry?.type, 'blob'); + expect(entry?.sha, '44b4fc6d56897b048c772eb4087f854f46256132'); + }); - verify(github.getJSON('/repos/o/n/git/tags/someSHA', - convert: (i) => GitTag.fromJson(i), statusCode: StatusCodes.OK)); - }); + test('code search', () async { + nock(fakeApiUrl) + .get( + '/search/code?q=search%20repo%3ASpinlockLabs%2Fgithub.dart%20in%3Afile&per_page=20') + .reply(200, nocked.searchResults); + + final results = (await github.search + .code( + 'search', + repo: 'SpinlockLabs/github.dart', + perPage: 20, + pages: 1, + ) + .toList()) + .first; + expect(results.totalCount, 17); + expect(results.items?.length, 17); }); - group('createTag()', () { - final createGitTag = CreateGitTag('v0.0.1', 'a message', someSha, 'commit', - GitCommitUser('aName', 'aEmail', DateTime.now())); + group('Merge', () { + test('Merge() normal', () async { + nock(fakeApiUrl) + .put('/repos/o/n/pulls/1/merge', '{"merge_method":"merge"}') + .reply(201, nocked.mergedPR1); - test('constructs correct path', () { - git.createTag(repo, createGitTag); + var pullRequestMerge = await github.pullRequests.merge(repo, 1); - verify(github.postJSON('/repos/o/n/git/tags', - convert: (i) => GitTag.fromJson(i), - statusCode: StatusCodes.CREATED, - body: GitHubJson.encode(createGitTag))); + expect(pullRequestMerge.merged, true); + expect(pullRequestMerge.message, 'Pull Request successfully merged'); + expect(pullRequestMerge.sha, someSha); }); - test('creates valid JSON body', () { - git.createTag(repo, createGitTag); + test('Merge() with squash', () async { + nock(fakeApiUrl) + .put('/repos/o/n/pulls/1/merge', '{"merge_method":"squash"}') + .reply(201, nocked.mergedPR1); - final body = captureSentBody(github); - expect(body['tag'], equals('v0.0.1')); - expect(body['message'], equals('a message')); - expect(body['object'], equals(someSha)); - expect(body['type'], equals('commit')); - expect(body['tagger']['name'], equals('aName')); - }); - }); + var pullRequestMerge = await github.pullRequests + .merge(repo, 1, mergeMethod: MergeMethod.squash); - group('getTree()', () { - test('constructs correct path', () { - git.getTree(repo, 'sh'); - - verify(github.getJSON('/repos/o/n/git/trees/sh', - convert: (j) => GitTree.fromJson(j), statusCode: StatusCodes.OK)); + expect(pullRequestMerge.merged, true); + expect(pullRequestMerge.message, 'Pull Request successfully merged'); + expect(pullRequestMerge.sha, someSha); }); - }); - - group('getTree(recursive: true)', () { - test('constructs correct path', () { - git.getTree(repo, 'sh', recursive: true); - verify(github.getJSON('/repos/o/n/git/trees/sh?recursive=1', - convert: (j) => GitTree.fromJson(j), statusCode: StatusCodes.OK)); - }); - }); + test('Merge() with rebase', () async { + nock(fakeApiUrl) + .put('/repos/o/n/pulls/1/merge', '{"merge_method":"rebase"}') + .reply(201, nocked.mergedPR1); - group('createTree()', () { - test('constructs correct path', () { - final createGitTree = CreateGitTree([]); - git.createTree(repo, createGitTree); + var pullRequestMerge = await github.pullRequests + .merge(repo, 1, mergeMethod: MergeMethod.rebase); - verify(github.postJSON('/repos/o/n/git/trees', - convert: (j) => GitTree.fromJson(j), - statusCode: StatusCodes.CREATED, - body: GitHubJson.encode(createGitTree))); + expect(pullRequestMerge.merged, true); + expect(pullRequestMerge.message, 'Pull Request successfully merged'); + expect(pullRequestMerge.sha, someSha); }); - test('with sha creates valid JSON body', () { - // given - var treeEntry = CreateGitTreeEntry('file.rb', '100644', 'blob', - sha: '44b4fc6d56897b048c772eb4087f854f46256132'); - - final tree = CreateGitTree([treeEntry]); - - // when - git.createTree(repo, tree); - - // then - final body = captureSentBody(github); - expect(body['tree'], isNotNull); - expect(body['tree'][0]['path'], equals('file.rb')); - expect(body['tree'][0]['mode'], equals('100644')); - expect(body['tree'][0]['type'], equals('blob')); - expect(body['tree'][0]['sha'], - equals('44b4fc6d56897b048c772eb4087f854f46256132')); - expect(body['tree'][0]['content'], isNull); - }); + test('Merge() with commitMessage', () async { + const commitMessage = 'Some message'; + nock(fakeApiUrl) + .put('/repos/o/n/pulls/1/merge', + '{"commit_message":"$commitMessage","merge_method":"squash"}') + .reply(201, nocked.mergedPR1); - test('with content creates valid JSON body', () { - // given - var treeEntry = CreateGitTreeEntry('file.rb', '100644', 'blob', - content: 'some file content'); + var pullRequestMerge = await github.pullRequests.merge(repo, 1, + message: commitMessage, mergeMethod: MergeMethod.squash); - final tree = CreateGitTree([treeEntry]); - - // when - git.createTree(repo, tree); + expect(pullRequestMerge.merged, true); + expect(pullRequestMerge.message, 'Pull Request successfully merged'); + expect(pullRequestMerge.sha, someSha); + }); - // then - final body = captureSentBody(github); - expect(body['tree'], isNotNull); - expect(body['tree'][0]['path'], equals('file.rb')); - expect(body['tree'][0]['mode'], equals('100644')); - expect(body['tree'][0]['type'], equals('blob')); - expect(body['tree'][0]['sha'], isNull); - expect(body['tree'][0]['content'], equals('some file content')); + test('Merge() with commitMessage, with sha', () async { + const commitMessage = 'Some message'; + const commitSha = 'commitSha'; + nock(fakeApiUrl) + .put('/repos/o/n/pulls/1/merge', + '{"commit_message":"$commitMessage","sha":"$commitSha","merge_method":"squash"}') + .reply(201, nocked.mergedPR1); + + var pullRequestMerge = await github.pullRequests.merge(repo, 1, + message: commitMessage, + mergeMethod: MergeMethod.squash, + requestSha: commitSha); + + expect(pullRequestMerge.merged, true); + expect(pullRequestMerge.message, 'Pull Request successfully merged'); + expect(pullRequestMerge.sha, someSha); }); }); } - -Map captureSentBody(MockGitHub github) { - final bodyString = verify(github.postJSON( - any, - convert: any, - statusCode: any, - body: captureAny, - )).captured.single; - - final body = jsonDecode(bodyString) as Map; - return body; -} diff --git a/test/helper/http.dart b/test/helper/http.dart index 566611d3..ffed40d4 100644 --- a/test/helper/http.dart +++ b/test/helper/http.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:http/http.dart' as http; import 'assets.dart'; @@ -12,10 +13,9 @@ class MockHTTPClient extends http.BaseClient { @override Future send(http.BaseRequest request) async { - final matchingUrlCreatorKey = responses.keys.firstWhere( - (it) => it.allMatches(request.url.toString()).isNotEmpty, - orElse: () => null); - final creator = responses[matchingUrlCreatorKey]; + final matchingUrlCreatorKey = responses.keys.firstWhereOrNull( + (it) => it.allMatches(request.url.toString()).isNotEmpty); + final creator = responses[matchingUrlCreatorKey!]; if (creator == null) { throw Exception('No Response Configured'); } @@ -25,8 +25,8 @@ class MockHTTPClient extends http.BaseClient { } class MockResponse extends http.Response { - MockResponse(String body, Map headers, int statusCode) - : super(body, statusCode, headers: headers); + MockResponse(super.body, Map headers, super.statusCode) + : super(headers: headers); factory MockResponse.fromAsset(String name) { final responseData = @@ -35,13 +35,13 @@ class MockResponse extends http.Response { final headers = responseData['headers'] as Map; final dynamic body = responseData['body']; final int statusCode = responseData['statusCode']; - String actualBody; + String? actualBody; if (body is Map || body is List) { actualBody = jsonDecode(body); } else { actualBody = body.toString(); } - return MockResponse(actualBody, headers, statusCode); + return MockResponse(actualBody!, headers, statusCode); } } diff --git a/test/scenarios_test.dart b/test/scenarios_test.dart new file mode 100644 index 00000000..fb453845 --- /dev/null +++ b/test/scenarios_test.dart @@ -0,0 +1,143 @@ +// ignore_for_file: unused_local_variable + +@Tags(['scenarios']) +@TestOn('vm') +library; + +import 'dart:convert'; + +import 'package:github/github.dart'; +import 'package:http/http.dart' as http; +import 'package:test/test.dart'; + +final defaultFixtureServerUri = Uri.parse('http://localhost:3000/fixtures'); + +/// All folder names at @octokit/fixtures/scenarios/api.github.com +/// are valid values for [scenario]. +Future createGithubWithScenario(String scenario, + {Uri? fixtureServerUri}) async { + fixtureServerUri ??= defaultFixtureServerUri; + + // send a request to the fixtures server to get load a fixture + var resp = await http.post(fixtureServerUri, + headers: {'Content-Type': 'application/json'}, + body: '{"scenario": "$scenario"}'); + if (resp.statusCode < 200 || resp.statusCode >= 300) { + throw Exception( + 'Error loading scenario: $scenario\n${resp.statusCode}\n${resp.body}'); + } + var j = json.decode(resp.body); + return GitHub( + endpoint: j['url'], + auth: const Authentication.withToken( + '0000000000000000000000000000000000000001')); +} + +/// Run scenario tests against ockokits fixtures-server +/// https://github.com/octokit/fixtures-server +/// +/// These tests are a port of the rest.js ocktokit tests from +/// https://github.com/octokit/rest.js/tree/master/test/scenarios +/// +/// The fixture server must be running before running these tests +/// The easiest way is to install node and then run +/// npx octokit-fixtures-server +/// +/// TODO(robrbecker) Implement a fixture-server "light" in Dart +/// directly using nock so we can remove the dependency on node +/// and running a server in order to run tests +void main() { + test('add-and-remove-repository-collaborator', () async { + var gh = await createGithubWithScenario( + 'add-and-remove-repository-collaborator'); + // todo do test + }, skip: true); + test('add-labels-to-issue', () async { + var gh = await createGithubWithScenario('add-labels-to-issue'); + // todo do test + }, skip: true); + test('branch-protection', () async { + var gh = await createGithubWithScenario('branch-protection'); + // todo do test + }, skip: true); + test('create-file', () async { + var gh = await createGithubWithScenario('create-file'); + // todo do test + }, skip: true); + + test('create-status', () async { + var gh = await createGithubWithScenario('create-status'); + // todo do test + }, skip: true); + test('errors', () async { + var gh = await createGithubWithScenario('errors'); + // todo do test + }, skip: true); + test('get-archive', () async { + var gh = await createGithubWithScenario('get-archive'); + // todo do test + }, skip: true); + test('get-content', () async { + var gh = await createGithubWithScenario('get-content'); + // todo do test + }, skip: true); + + test('get-organization', () async { + var gh = await createGithubWithScenario('get-organization'); + var org = await gh.organizations.get('octokit-fixture-org'); + expect(org.login, 'octokit-fixture-org'); + }); + + test('get-repository', () async { + var gh = await createGithubWithScenario('get-repository'); + // todo do test + }, skip: true); + test('get-root', () async { + var gh = await createGithubWithScenario('get-root'); + // todo do test + }, skip: true); + test('git-refs', () async { + var gh = await createGithubWithScenario('git-refs'); + // todo do test + }, skip: true); + test('labels', () async { + var gh = await createGithubWithScenario('labels'); + // todo do test + }, skip: true); + test('lock-issue', () async { + var gh = await createGithubWithScenario('lock-issue'); + // todo do test + }, skip: true); + test('mark-notifications-as-read', () async { + var gh = await createGithubWithScenario('mark-notifications-as-read'); + // todo do test + }, skip: true); + test('markdown', () async { + var gh = await createGithubWithScenario('markdown'); + // todo do test + }, skip: true); + test('paginate-issues', () async { + var gh = await createGithubWithScenario('paginate-issues'); + // todo do test + }, skip: true); + test('project-cards', () async { + var gh = await createGithubWithScenario('project-cards'); + // todo do test + }, skip: true); + test('release-assets-conflict', () async { + var gh = await createGithubWithScenario('release-assets-conflict'); + // todo do test + }, skip: true); + test('release-assets', () async { + var gh = await createGithubWithScenario('release-assets'); + // todo do test + }, skip: true); + test('rename-repository', () async { + var gh = await createGithubWithScenario('rename-repository'); + // todo do test + }, skip: true); + test('search-issues', () async { + var gh = await createGithubWithScenario('search-issues'); + // todo do test + }, skip: true); +} diff --git a/test/server/hooks_test.dart b/test/server/hooks_test.dart new file mode 100644 index 00000000..d00cb3de --- /dev/null +++ b/test/server/hooks_test.dart @@ -0,0 +1,85 @@ +import 'dart:convert'; +import 'package:github/github.dart'; +import 'package:github/hooks.dart'; +import 'package:test/test.dart'; + +import 'hooks_test_data.dart'; + +void main() { + group('CheckSuiteEvent', () { + test('deserialize', () async { + final checkSuiteEvent = CheckSuiteEvent.fromJson( + json.decode(checkSuiteString) as Map); + // Top level properties. + expect(checkSuiteEvent.action, 'requested'); + expect(checkSuiteEvent.checkSuite, isA()); + // CheckSuite properties. + final suite = checkSuiteEvent.checkSuite!; + expect(suite.headSha, 'ec26c3e57ca3a959ca5aad62de7213c562f8c821'); + expect(suite.id, 118578147); + expect(suite.conclusion, CheckRunConclusion.success); + }); + }); + group('CheckRunEvent', () { + test('deserialize', () async { + final checkRunEvent = CheckRunEvent.fromJson( + json.decode(checkRunString) as Map); + // Top level properties. + expect(checkRunEvent.action, 'created'); + expect(checkRunEvent.checkRun, isA()); + // CheckSuite properties. + final checkRun = checkRunEvent.checkRun!; + expect(checkRun.headSha, 'ec26c3e57ca3a959ca5aad62de7213c562f8c821'); + expect(checkRun.checkSuiteId, 118578147); + expect(checkRun.detailsUrl, 'https://octocoders.io'); + expect(checkRun.externalId, ''); + expect(checkRun.id, 128620228); + expect(checkRun.name, 'Octocoders-linter'); + expect(checkRun.startedAt, DateTime.utc(2019, 05, 15, 15, 21, 12)); + expect(checkRun.status, CheckRunStatus.queued); + }); + }); + + group('CreateEvent', () { + test('deserialize', () async { + final createEvent = CreateEvent.fromJson( + json.decode(createString) as Map); + expect(createEvent.ref, 'simple-branch'); + expect(createEvent.refType, 'branch'); + expect(createEvent.pusherType, 'user'); + + final repo = createEvent.repository!; + expect(repo.slug().fullName, 'Codertocat/Hello-World'); + expect(repo.id, 186853002); + + final sender = createEvent.sender!; + expect(sender.login, "Codertocat"); + expect(sender.htmlUrl, "https://github.com/Codertocat"); + }); + }); + + group('EditedPullRequest', () { + test('deserialize with body edit', () { + final pullRequestEditedEvent = PullRequestEvent.fromJson( + jsonDecode(prBodyEditedEvent) as Map); + final changes = pullRequestEditedEvent.changes; + expect(changes, isNotNull); + expect(changes!.body!.from, isNotNull); + assert(changes.body!.from == + '**This should not land until https://github.com/flutter/buildroot/pull/790'); + }); + + test('deserialize with base edit', () { + final pullRequestEditedEvent = PullRequestEvent.fromJson( + jsonDecode(prBaseEditedEvent) as Map); + final changes = pullRequestEditedEvent.changes; + expect(changes, isNotNull); + expect(changes!.body, isNull); + expect(changes.base, isNotNull); + expect(changes.base!.ref, isNotNull); + assert(changes.base!.ref!.from == 'main'); + assert(changes.base!.sha!.from == + 'b3af5d64d3e6e2110b07d71909fc432537339659'); + }); + }); +} diff --git a/test/server/hooks_test_data.dart b/test/server/hooks_test_data.dart new file mode 100644 index 00000000..ad02cc10 --- /dev/null +++ b/test/server/hooks_test_data.dart @@ -0,0 +1,1818 @@ +/// Json messages as dart string used for checks model tests. +library; + +String checkSuiteString = checkSuiteTemplate('requested'); + +String checkSuiteTemplate(String action) => '''\ +{ + "action": "$action", + "check_suite": { + "id": 118578147, + "node_id": "MDEwOkNoZWNrU3VpdGUxMTg1NzgxNDc=", + "head_branch": "changes", + "head_sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "status": "completed", + "conclusion": "success", + "url": "https://api.github.com/repos/Codertocat/Hello-World/check-suites/118578147", + "before": "6113728f27ae82c7b1a177c8d03f9e96e0adf246", + "after": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "pull_requests": [ + { + "url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2", + "id": 279147437, + "number": 2, + "head": { + "ref": "changes", + "sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "repo": { + "id": 186853002, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "name": "Hello-World" + } + }, + "base": { + "ref": "master", + "sha": "f95f852bd8fca8fcc58a9a2d6c842781e32a215e", + "repo": { + "id": 186853002, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "name": "Hello-World" + } + } + } + ], + "app": { + "id": 29310, + "node_id": "MDM6QXBwMjkzMTA=", + "owner": { + "login": "Octocoders", + "id": 38302899, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjM4MzAyODk5", + "avatar_url": "https://avatars1.githubusercontent.com/u/38302899?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Octocoders", + "html_url": "https://github.com/Octocoders", + "followers_url": "https://api.github.com/users/Octocoders/followers", + "following_url": "https://api.github.com/users/Octocoders/following{/other_user}", + "gists_url": "https://api.github.com/users/Octocoders/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Octocoders/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Octocoders/subscriptions", + "organizations_url": "https://api.github.com/users/Octocoders/orgs", + "repos_url": "https://api.github.com/users/Octocoders/repos", + "events_url": "https://api.github.com/users/Octocoders/events{/privacy}", + "received_events_url": "https://api.github.com/users/Octocoders/received_events", + "type": "Organization", + "site_admin": false + }, + "name": "octocoders-linter", + "description": "", + "external_url": "https://octocoders.io", + "html_url": "https://github.com/apps/octocoders-linter", + "created_at": "2019-04-19T19:36:24Z", + "updated_at": "2019-04-19T19:36:56Z", + "permissions": { + "administration": "write", + "checks": "write", + "contents": "write", + "deployments": "write", + "issues": "write", + "members": "write", + "metadata": "read", + "organization_administration": "write", + "organization_hooks": "write", + "organization_plan": "read", + "organization_projects": "write", + "organization_user_blocking": "write", + "pages": "write", + "pull_requests": "write", + "repository_hooks": "write", + "repository_projects": "write", + "statuses": "write", + "team_discussions": "write", + "vulnerability_alerts": "read" + }, + "events": [] + }, + "created_at": "2019-05-15T15:20:31Z", + "updated_at": "2019-05-15T15:21:14Z", + "latest_check_runs_count": 1, + "check_runs_url": "https://api.github.com/repos/Codertocat/Hello-World/check-suites/118578147/check-runs", + "head_commit": { + "id": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "tree_id": "31b122c26a97cf9af023e9ddab94a82c6e77b0ea", + "message": "Update README.md", + "timestamp": "2019-05-15T15:20:30Z", + "author": { + "name": "Codertocat", + "email": "21031067+Codertocat@users.noreply.github.com" + }, + "committer": { + "name": "Codertocat", + "email": "21031067+Codertocat@users.noreply.github.com" + } + } + }, + "repository": { + "id": 186853002, + "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": false, + "owner": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Codertocat/Hello-World", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks", + "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams", + "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks", + "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}", + "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events", + "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}", + "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}", + "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags", + "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages", + "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers", + "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors", + "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription", + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}", + "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges", + "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads", + "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}", + "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}", + "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}", + "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments", + "created_at": "2019-05-15T15:19:25Z", + "updated_at": "2019-05-15T15:21:14Z", + "pushed_at": "2019-05-15T15:20:57Z", + "git_url": "git://github.com/Codertocat/Hello-World.git", + "ssh_url": "git@github.com:Codertocat/Hello-World.git", + "clone_url": "https://github.com/Codertocat/Hello-World.git", + "svn_url": "https://github.com/Codertocat/Hello-World", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Ruby", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": null, + "forks": 0, + "open_issues": 2, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} +'''; + +const String checkRunString = ''' +{ + "action": "created", + "check_run": { + "id": 128620228, + "node_id": "MDg6Q2hlY2tSdW4xMjg2MjAyMjg=", + "head_sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "external_id": "", + "url": "https://api.github.com/repos/Codertocat/Hello-World/check-runs/128620228", + "html_url": "https://github.com/Codertocat/Hello-World/runs/128620228", + "details_url": "https://octocoders.io", + "status": "queued", + "conclusion": null, + "started_at": "2019-05-15T15:21:12Z", + "completed_at": null, + "output": { + "title": null, + "summary": null, + "text": null, + "annotations_count": 0, + "annotations_url": "https://api.github.com/repos/Codertocat/Hello-World/check-runs/128620228/annotations" + }, + "name": "Octocoders-linter", + "check_suite": { + "id": 118578147, + "node_id": "MDEwOkNoZWNrU3VpdGUxMTg1NzgxNDc=", + "head_branch": "changes", + "head_sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "status": "queued", + "conclusion": null, + "url": "https://api.github.com/repos/Codertocat/Hello-World/check-suites/118578147", + "before": "6113728f27ae82c7b1a177c8d03f9e96e0adf246", + "after": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "pull_requests": [ + { + "url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2", + "id": 279147437, + "number": 2, + "head": { + "ref": "changes", + "sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "repo": { + "id": 186853002, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "name": "Hello-World" + } + }, + "base": { + "ref": "master", + "sha": "f95f852bd8fca8fcc58a9a2d6c842781e32a215e", + "repo": { + "id": 186853002, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "name": "Hello-World" + } + } + } + ], + "app": { + "id": 29310, + "node_id": "MDM6QXBwMjkzMTA=", + "owner": { + "login": "Octocoders", + "id": 38302899, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjM4MzAyODk5", + "avatar_url": "https://avatars1.githubusercontent.com/u/38302899?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Octocoders", + "html_url": "https://github.com/Octocoders", + "followers_url": "https://api.github.com/users/Octocoders/followers", + "following_url": "https://api.github.com/users/Octocoders/following{/other_user}", + "gists_url": "https://api.github.com/users/Octocoders/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Octocoders/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Octocoders/subscriptions", + "organizations_url": "https://api.github.com/users/Octocoders/orgs", + "repos_url": "https://api.github.com/users/Octocoders/repos", + "events_url": "https://api.github.com/users/Octocoders/events{/privacy}", + "received_events_url": "https://api.github.com/users/Octocoders/received_events", + "type": "Organization", + "site_admin": false + }, + "name": "octocoders-linter", + "description": "", + "external_url": "https://octocoders.io", + "html_url": "https://github.com/apps/octocoders-linter", + "created_at": "2019-04-19T19:36:24Z", + "updated_at": "2019-04-19T19:36:56Z", + "permissions": { + "administration": "write", + "checks": "write", + "contents": "write", + "deployments": "write", + "issues": "write", + "members": "write", + "metadata": "read", + "organization_administration": "write", + "organization_hooks": "write", + "organization_plan": "read", + "organization_projects": "write", + "organization_user_blocking": "write", + "pages": "write", + "pull_requests": "write", + "repository_hooks": "write", + "repository_projects": "write", + "statuses": "write", + "team_discussions": "write", + "vulnerability_alerts": "read" + }, + "events": [] + }, + "created_at": "2019-05-15T15:20:31Z", + "updated_at": "2019-05-15T15:20:31Z" + }, + "app": { + "id": 29310, + "node_id": "MDM6QXBwMjkzMTA=", + "owner": { + "login": "Octocoders", + "id": 38302899, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjM4MzAyODk5", + "avatar_url": "https://avatars1.githubusercontent.com/u/38302899?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Octocoders", + "html_url": "https://github.com/Octocoders", + "followers_url": "https://api.github.com/users/Octocoders/followers", + "following_url": "https://api.github.com/users/Octocoders/following{/other_user}", + "gists_url": "https://api.github.com/users/Octocoders/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Octocoders/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Octocoders/subscriptions", + "organizations_url": "https://api.github.com/users/Octocoders/orgs", + "repos_url": "https://api.github.com/users/Octocoders/repos", + "events_url": "https://api.github.com/users/Octocoders/events{/privacy}", + "received_events_url": "https://api.github.com/users/Octocoders/received_events", + "type": "Organization", + "site_admin": false + }, + "name": "octocoders-linter", + "description": "", + "external_url": "https://octocoders.io", + "html_url": "https://github.com/apps/octocoders-linter", + "created_at": "2019-04-19T19:36:24Z", + "updated_at": "2019-04-19T19:36:56Z", + "permissions": { + "administration": "write", + "checks": "write", + "contents": "write", + "deployments": "write", + "issues": "write", + "members": "write", + "metadata": "read", + "organization_administration": "write", + "organization_hooks": "write", + "organization_plan": "read", + "organization_projects": "write", + "organization_user_blocking": "write", + "pages": "write", + "pull_requests": "write", + "repository_hooks": "write", + "repository_projects": "write", + "statuses": "write", + "team_discussions": "write", + "vulnerability_alerts": "read" + }, + "events": [] + }, + "pull_requests": [ + { + "url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2", + "id": 279147437, + "number": 2, + "head": { + "ref": "changes", + "sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "repo": { + "id": 186853002, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "name": "Hello-World" + } + }, + "base": { + "ref": "master", + "sha": "f95f852bd8fca8fcc58a9a2d6c842781e32a215e", + "repo": { + "id": 186853002, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "name": "Hello-World" + } + } + } + ], + "deployment": { + "url": "https://api.github.com/repos/Codertocat/Hello-World/deployments/326191728", + "id": 326191728, + "node_id": "MDEwOkRlcGxveW1lbnQzMjYxOTE3Mjg=", + "task": "deploy", + "original_environment": "lab", + "environment": "lab", + "description": null, + "created_at": "2021-02-18T08:22:48Z", + "updated_at": "2021-02-18T09:47:16Z", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments/326191728/statuses", + "repository_url": "https://api.github.com/repos/Codertocat/Hello-World" + } + }, + "repository": { + "id": 186853002, + "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": false, + "owner": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Codertocat/Hello-World", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks", + "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams", + "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks", + "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}", + "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events", + "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}", + "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}", + "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags", + "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages", + "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers", + "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors", + "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription", + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}", + "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges", + "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads", + "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}", + "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}", + "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}", + "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments", + "created_at": "2019-05-15T15:19:25Z", + "updated_at": "2019-05-15T15:21:03Z", + "pushed_at": "2019-05-15T15:20:57Z", + "git_url": "git://github.com/Codertocat/Hello-World.git", + "ssh_url": "git@github.com:Codertocat/Hello-World.git", + "clone_url": "https://github.com/Codertocat/Hello-World.git", + "svn_url": "https://github.com/Codertocat/Hello-World", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Ruby", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 1, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": null, + "forks": 1, + "open_issues": 2, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} +'''; + +const String createString = ''' +{ + "ref": "simple-branch", + "ref_type": "branch", + "master_branch": "master", + "description": null, + "pusher_type": "user", + "repository": { + "id": 186853002, + "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": false, + "owner": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Codertocat/Hello-World", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks", + "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams", + "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks", + "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}", + "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events", + "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}", + "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}", + "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags", + "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages", + "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers", + "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors", + "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription", + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}", + "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges", + "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads", + "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}", + "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}", + "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}", + "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments", + "created_at": "2019-05-15T15:19:25Z", + "updated_at": "2019-05-15T15:20:41Z", + "pushed_at": "2019-05-15T15:20:56Z", + "git_url": "git://github.com/Codertocat/Hello-World.git", + "ssh_url": "git@github.com:Codertocat/Hello-World.git", + "clone_url": "https://github.com/Codertocat/Hello-World.git", + "svn_url": "https://github.com/Codertocat/Hello-World", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Ruby", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 1, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": null, + "forks": 1, + "open_issues": 2, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} +'''; + +const String prBodyEditedEvent = ''' +{ + "action": "edited", + "number": 47609, + "pull_request": { + "url": "https://api.github.com/repos/flutter/engine/pulls/47609", + "id": 1584723957, + "node_id": "PR_kwDOAlZRSc5edPf1", + "html_url": "https://github.com/flutter/engine/pull/47609", + "diff_url": "https://github.com/flutter/engine/pull/47609.diff", + "patch_url": "https://github.com/flutter/engine/pull/47609.patch", + "issue_url": "https://api.github.com/repos/flutter/engine/issues/47609", + "number": 47609, + "state": "open", + "locked": false, + "title": "Upgrade Android SDK to 34 UpsideDownCake", + "user": { + "login": "gmackall", + "id": 34871572, + "node_id": "MDQ6VXNlcjM0ODcxNTcy", + "avatar_url": "https://avatars.githubusercontent.com/u/34871572?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/gmackall", + "html_url": "https://github.com/gmackall", + "followers_url": "https://api.github.com/users/gmackall/followers", + "following_url": "https://api.github.com/users/gmackall/following{/other_user}", + "gists_url": "https://api.github.com/users/gmackall/gists{/gist_id}", + "starred_url": "https://api.github.com/users/gmackall/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/gmackall/subscriptions", + "organizations_url": "https://api.github.com/users/gmackall/orgs", + "repos_url": "https://api.github.com/users/gmackall/repos", + "events_url": "https://api.github.com/users/gmackall/events{/privacy}", + "received_events_url": "https://api.github.com/users/gmackall/received_events", + "type": "User", + "site_admin": false + }, + "body": "~**This should not land until https://github.com/flutter/buildroot/pull/790 (re)lands, and I swap the buildroot url back to the latest commit.**~ Reland of PR to update buildroot at https://github.com/flutter/buildroot/pull/792. Upgrades to android api 34 Also: 1. Upgrades to java 17 in DEPS/ci", + "created_at": "2023-11-02T17:09:59Z", + "updated_at": "2023-11-08T21:00:47Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": "8e5e3a59a5cba4239c542ed9a914899a246640b7", + "assignee": null, + "assignees": [ + + ], + "requested_reviewers": [ + + ], + "requested_teams": [ + + ], + "labels": [ + { + "id": 246348935, + "node_id": "MDU6TGFiZWwyNDYzNDg5MzU=", + "url": "https://api.github.com/repos/flutter/engine/labels/platform-android", + "name": "platform-android", + "color": "A4C639", + "default": false, + "description": null + } + ], + "milestone": null, + "draft": false, + "commits_url": "https://api.github.com/repos/flutter/engine/pulls/47609/commits", + "review_comments_url": "https://api.github.com/repos/flutter/engine/pulls/47609/comments", + "review_comment_url": "https://api.github.com/repos/flutter/engine/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/flutter/engine/issues/47609/comments", + "statuses_url": "https://api.github.com/repos/flutter/engine/statuses/a6765b4c309aa082bbebade68e0c7ec308a1cc6c", + "head": { + "label": "gmackall:upgrade_to_android14", + "ref": "upgrade_to_android14", + "sha": "a6765b4c309aa082bbebade68e0c7ec308a1cc6c", + "user": { + "login": "gmackall", + "id": 34871572, + "node_id": "MDQ6VXNlcjM0ODcxNTcy", + "avatar_url": "https://avatars.githubusercontent.com/u/34871572?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/gmackall", + "html_url": "https://github.com/gmackall", + "followers_url": "https://api.github.com/users/gmackall/followers", + "following_url": "https://api.github.com/users/gmackall/following{/other_user}", + "gists_url": "https://api.github.com/users/gmackall/gists{/gist_id}", + "starred_url": "https://api.github.com/users/gmackall/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/gmackall/subscriptions", + "organizations_url": "https://api.github.com/users/gmackall/orgs", + "repos_url": "https://api.github.com/users/gmackall/repos", + "events_url": "https://api.github.com/users/gmackall/events{/privacy}", + "received_events_url": "https://api.github.com/users/gmackall/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 547558963, + "node_id": "R_kgDOIKMWMw", + "name": "engine", + "full_name": "gmackall/engine", + "private": false, + "owner": { + "login": "gmackall", + "id": 34871572, + "node_id": "MDQ6VXNlcjM0ODcxNTcy", + "avatar_url": "https://avatars.githubusercontent.com/u/34871572?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/gmackall", + "html_url": "https://github.com/gmackall", + "followers_url": "https://api.github.com/users/gmackall/followers", + "following_url": "https://api.github.com/users/gmackall/following{/other_user}", + "gists_url": "https://api.github.com/users/gmackall/gists{/gist_id}", + "starred_url": "https://api.github.com/users/gmackall/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/gmackall/subscriptions", + "organizations_url": "https://api.github.com/users/gmackall/orgs", + "repos_url": "https://api.github.com/users/gmackall/repos", + "events_url": "https://api.github.com/users/gmackall/events{/privacy}", + "received_events_url": "https://api.github.com/users/gmackall/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/gmackall/engine", + "description": "The Flutter engine", + "fork": true, + "url": "https://api.github.com/repos/gmackall/engine", + "forks_url": "https://api.github.com/repos/gmackall/engine/forks", + "keys_url": "https://api.github.com/repos/gmackall/engine/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/gmackall/engine/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/gmackall/engine/teams", + "hooks_url": "https://api.github.com/repos/gmackall/engine/hooks", + "issue_events_url": "https://api.github.com/repos/gmackall/engine/issues/events{/number}", + "events_url": "https://api.github.com/repos/gmackall/engine/events", + "assignees_url": "https://api.github.com/repos/gmackall/engine/assignees{/user}", + "branches_url": "https://api.github.com/repos/gmackall/engine/branches{/branch}", + "tags_url": "https://api.github.com/repos/gmackall/engine/tags", + "blobs_url": "https://api.github.com/repos/gmackall/engine/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/gmackall/engine/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/gmackall/engine/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/gmackall/engine/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/gmackall/engine/statuses/{sha}", + "languages_url": "https://api.github.com/repos/gmackall/engine/languages", + "stargazers_url": "https://api.github.com/repos/gmackall/engine/stargazers", + "contributors_url": "https://api.github.com/repos/gmackall/engine/contributors", + "subscribers_url": "https://api.github.com/repos/gmackall/engine/subscribers", + "subscription_url": "https://api.github.com/repos/gmackall/engine/subscription", + "commits_url": "https://api.github.com/repos/gmackall/engine/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/gmackall/engine/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/gmackall/engine/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/gmackall/engine/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/gmackall/engine/contents/{+path}", + "compare_url": "https://api.github.com/repos/gmackall/engine/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/gmackall/engine/merges", + "archive_url": "https://api.github.com/repos/gmackall/engine/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/gmackall/engine/downloads", + "issues_url": "https://api.github.com/repos/gmackall/engine/issues{/number}", + "pulls_url": "https://api.github.com/repos/gmackall/engine/pulls{/number}", + "milestones_url": "https://api.github.com/repos/gmackall/engine/milestones{/number}", + "notifications_url": "https://api.github.com/repos/gmackall/engine/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/gmackall/engine/labels{/name}", + "releases_url": "https://api.github.com/repos/gmackall/engine/releases{/id}", + "deployments_url": "https://api.github.com/repos/gmackall/engine/deployments", + "created_at": "2022-10-07T22:25:57Z", + "updated_at": "2023-02-02T18:38:07Z", + "pushed_at": "2023-11-08T20:57:02Z", + "git_url": "git://github.com/gmackall/engine.git", + "ssh_url": "git@github.com:gmackall/engine.git", + "clone_url": "https://github.com/gmackall/engine.git", + "svn_url": "https://github.com/gmackall/engine", + "homepage": "https://flutter.dev", + "size": 466778, + "stargazers_count": 0, + "watchers_count": 0, + "language": "C++", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "has_discussions": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause New or Revised License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + + ], + "visibility": "public", + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main", + "allow_squash_merge": true, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": false, + "allow_update_branch": false, + "use_squash_pr_title_as_default": false, + "squash_merge_commit_message": "COMMIT_MESSAGES", + "squash_merge_commit_title": "COMMIT_OR_PR_TITLE", + "merge_commit_message": "PR_TITLE", + "merge_commit_title": "MERGE_MESSAGE" + } + }, + "base": { + "label": "flutter:main", + "ref": "main", + "sha": "941e246d4851f652cf13312180174ebc9395fac4", + "user": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/flutter", + "html_url": "https://github.com/flutter", + "followers_url": "https://api.github.com/users/flutter/followers", + "following_url": "https://api.github.com/users/flutter/following{/other_user}", + "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", + "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", + "organizations_url": "https://api.github.com/users/flutter/orgs", + "repos_url": "https://api.github.com/users/flutter/repos", + "events_url": "https://api.github.com/users/flutter/events{/privacy}", + "received_events_url": "https://api.github.com/users/flutter/received_events", + "type": "Organization", + "site_admin": false + }, + "repo": { + "id": 39211337, + "node_id": "MDEwOlJlcG9zaXRvcnkzOTIxMTMzNw==", + "name": "engine", + "full_name": "flutter/engine", + "private": false, + "owner": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/flutter", + "html_url": "https://github.com/flutter", + "followers_url": "https://api.github.com/users/flutter/followers", + "following_url": "https://api.github.com/users/flutter/following{/other_user}", + "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", + "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", + "organizations_url": "https://api.github.com/users/flutter/orgs", + "repos_url": "https://api.github.com/users/flutter/repos", + "events_url": "https://api.github.com/users/flutter/events{/privacy}", + "received_events_url": "https://api.github.com/users/flutter/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/flutter/engine", + "description": "The Flutter engine", + "fork": false, + "url": "https://api.github.com/repos/flutter/engine", + "forks_url": "https://api.github.com/repos/flutter/engine/forks", + "keys_url": "https://api.github.com/repos/flutter/engine/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/flutter/engine/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/flutter/engine/teams", + "hooks_url": "https://api.github.com/repos/flutter/engine/hooks", + "issue_events_url": "https://api.github.com/repos/flutter/engine/issues/events{/number}", + "events_url": "https://api.github.com/repos/flutter/engine/events", + "assignees_url": "https://api.github.com/repos/flutter/engine/assignees{/user}", + "branches_url": "https://api.github.com/repos/flutter/engine/branches{/branch}", + "tags_url": "https://api.github.com/repos/flutter/engine/tags", + "blobs_url": "https://api.github.com/repos/flutter/engine/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/flutter/engine/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/flutter/engine/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/flutter/engine/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/flutter/engine/statuses/{sha}", + "languages_url": "https://api.github.com/repos/flutter/engine/languages", + "stargazers_url": "https://api.github.com/repos/flutter/engine/stargazers", + "contributors_url": "https://api.github.com/repos/flutter/engine/contributors", + "subscribers_url": "https://api.github.com/repos/flutter/engine/subscribers", + "subscription_url": "https://api.github.com/repos/flutter/engine/subscription", + "commits_url": "https://api.github.com/repos/flutter/engine/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/flutter/engine/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/flutter/engine/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/flutter/engine/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/flutter/engine/contents/{+path}", + "compare_url": "https://api.github.com/repos/flutter/engine/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/flutter/engine/merges", + "archive_url": "https://api.github.com/repos/flutter/engine/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/flutter/engine/downloads", + "issues_url": "https://api.github.com/repos/flutter/engine/issues{/number}", + "pulls_url": "https://api.github.com/repos/flutter/engine/pulls{/number}", + "milestones_url": "https://api.github.com/repos/flutter/engine/milestones{/number}", + "notifications_url": "https://api.github.com/repos/flutter/engine/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/flutter/engine/labels{/name}", + "releases_url": "https://api.github.com/repos/flutter/engine/releases{/id}", + "deployments_url": "https://api.github.com/repos/flutter/engine/deployments", + "created_at": "2015-07-16T17:39:56Z", + "updated_at": "2023-11-08T07:16:25Z", + "pushed_at": "2023-11-08T21:00:38Z", + "git_url": "git://github.com/flutter/engine.git", + "ssh_url": "git@github.com:flutter/engine.git", + "clone_url": "https://github.com/flutter/engine.git", + "svn_url": "https://github.com/flutter/engine", + "homepage": "https://flutter.dev", + "size": 704170, + "stargazers_count": 6848, + "watchers_count": 6848, + "language": "C++", + "has_issues": false, + "has_projects": false, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "has_discussions": false, + "forks_count": 5600, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 99, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause New or Revised License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + "c-plus-plus" + ], + "visibility": "public", + "forks": 5600, + "open_issues": 99, + "watchers": 6848, + "default_branch": "main", + "allow_squash_merge": true, + "allow_merge_commit": false, + "allow_rebase_merge": false, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_update_branch": true, + "use_squash_pr_title_as_default": true, + "squash_merge_commit_message": "PR_BODY", + "squash_merge_commit_title": "PR_TITLE", + "merge_commit_message": "PR_TITLE", + "merge_commit_title": "MERGE_MESSAGE" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/flutter/engine/pulls/47609" + }, + "html": { + "href": "https://github.com/flutter/engine/pull/47609" + }, + "issue": { + "href": "https://api.github.com/repos/flutter/engine/issues/47609" + }, + "comments": { + "href": "https://api.github.com/repos/flutter/engine/issues/47609/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/flutter/engine/pulls/47609/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/flutter/engine/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/flutter/engine/pulls/47609/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/flutter/engine/statuses/a6765b4c309aa082bbebade68e0c7ec308a1cc6c" + } + }, + "author_association": "MEMBER", + "auto_merge": null, + "active_lock_reason": null, + "merged": false, + "mergeable": true, + "rebaseable": true, + "mergeable_state": "clean", + "merged_by": null, + "comments": 4, + "review_comments": 4, + "maintainer_can_modify": true, + "commits": 32, + "additions": 83, + "deletions": 77, + "changed_files": 18 + }, + "changes": { + "body": { + "from": "**This should not land until https://github.com/flutter/buildroot/pull/790" + } + }, + "repository": { + "id": 39211337, + "node_id": "MDEwOlJlcG9zaXRvcnkzOTIxMTMzNw==", + "name": "engine", + "full_name": "flutter/engine", + "private": false, + "owner": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/flutter", + "html_url": "https://github.com/flutter", + "followers_url": "https://api.github.com/users/flutter/followers", + "following_url": "https://api.github.com/users/flutter/following{/other_user}", + "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", + "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", + "organizations_url": "https://api.github.com/users/flutter/orgs", + "repos_url": "https://api.github.com/users/flutter/repos", + "events_url": "https://api.github.com/users/flutter/events{/privacy}", + "received_events_url": "https://api.github.com/users/flutter/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/flutter/engine", + "description": "The Flutter engine", + "fork": false, + "url": "https://api.github.com/repos/flutter/engine", + "forks_url": "https://api.github.com/repos/flutter/engine/forks", + "keys_url": "https://api.github.com/repos/flutter/engine/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/flutter/engine/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/flutter/engine/teams", + "hooks_url": "https://api.github.com/repos/flutter/engine/hooks", + "issue_events_url": "https://api.github.com/repos/flutter/engine/issues/events{/number}", + "events_url": "https://api.github.com/repos/flutter/engine/events", + "assignees_url": "https://api.github.com/repos/flutter/engine/assignees{/user}", + "branches_url": "https://api.github.com/repos/flutter/engine/branches{/branch}", + "tags_url": "https://api.github.com/repos/flutter/engine/tags", + "blobs_url": "https://api.github.com/repos/flutter/engine/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/flutter/engine/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/flutter/engine/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/flutter/engine/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/flutter/engine/statuses/{sha}", + "languages_url": "https://api.github.com/repos/flutter/engine/languages", + "stargazers_url": "https://api.github.com/repos/flutter/engine/stargazers", + "contributors_url": "https://api.github.com/repos/flutter/engine/contributors", + "subscribers_url": "https://api.github.com/repos/flutter/engine/subscribers", + "subscription_url": "https://api.github.com/repos/flutter/engine/subscription", + "commits_url": "https://api.github.com/repos/flutter/engine/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/flutter/engine/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/flutter/engine/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/flutter/engine/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/flutter/engine/contents/{+path}", + "compare_url": "https://api.github.com/repos/flutter/engine/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/flutter/engine/merges", + "archive_url": "https://api.github.com/repos/flutter/engine/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/flutter/engine/downloads", + "issues_url": "https://api.github.com/repos/flutter/engine/issues{/number}", + "pulls_url": "https://api.github.com/repos/flutter/engine/pulls{/number}", + "milestones_url": "https://api.github.com/repos/flutter/engine/milestones{/number}", + "notifications_url": "https://api.github.com/repos/flutter/engine/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/flutter/engine/labels{/name}", + "releases_url": "https://api.github.com/repos/flutter/engine/releases{/id}", + "deployments_url": "https://api.github.com/repos/flutter/engine/deployments", + "created_at": "2015-07-16T17:39:56Z", + "updated_at": "2023-11-08T07:16:25Z", + "pushed_at": "2023-11-08T21:00:38Z", + "git_url": "git://github.com/flutter/engine.git", + "ssh_url": "git@github.com:flutter/engine.git", + "clone_url": "https://github.com/flutter/engine.git", + "svn_url": "https://github.com/flutter/engine", + "homepage": "https://flutter.dev", + "size": 704170, + "stargazers_count": 6848, + "watchers_count": 6848, + "language": "C++", + "has_issues": false, + "has_projects": false, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "has_discussions": false, + "forks_count": 5600, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 99, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause New or Revised License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + "c-plus-plus" + ], + "visibility": "public", + "forks": 5600, + "open_issues": 99, + "watchers": 6848, + "default_branch": "main", + "custom_properties": { + + } + }, + "organization": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "url": "https://api.github.com/orgs/flutter", + "repos_url": "https://api.github.com/orgs/flutter/repos", + "events_url": "https://api.github.com/orgs/flutter/events", + "hooks_url": "https://api.github.com/orgs/flutter/hooks", + "issues_url": "https://api.github.com/orgs/flutter/issues", + "members_url": "https://api.github.com/orgs/flutter/members{/member}", + "public_members_url": "https://api.github.com/orgs/flutter/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "description": "Flutter is Google's UI toolkit for building beautiful, natively compiled applications for mobile, web, desktop, and embedded devices from a single codebase." + }, + "enterprise": { + "id": 1732, + "slug": "alphabet", + "name": "Alphabet", + "node_id": "MDEwOkVudGVycHJpc2UxNzMy", + "avatar_url": "https://avatars.githubusercontent.com/b/1732?v=4", + "description": "", + "website_url": "https://abc.xyz/", + "html_url": "https://github.com/enterprises/alphabet", + "created_at": "2019-12-19T00:30:52Z", + "updated_at": "2023-01-20T00:41:48Z" + }, + "sender": { + "login": "gmackall", + "id": 34871572, + "node_id": "MDQ6VXNlcjM0ODcxNTcy", + "avatar_url": "https://avatars.githubusercontent.com/u/34871572?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/gmackall", + "html_url": "https://github.com/gmackall", + "followers_url": "https://api.github.com/users/gmackall/followers", + "following_url": "https://api.github.com/users/gmackall/following{/other_user}", + "gists_url": "https://api.github.com/users/gmackall/gists{/gist_id}", + "starred_url": "https://api.github.com/users/gmackall/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/gmackall/subscriptions", + "organizations_url": "https://api.github.com/users/gmackall/orgs", + "repos_url": "https://api.github.com/users/gmackall/repos", + "events_url": "https://api.github.com/users/gmackall/events{/privacy}", + "received_events_url": "https://api.github.com/users/gmackall/received_events", + "type": "User", + "site_admin": false + }, + "installation": { + "id": 10381585, + "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMTAzODE1ODU=" + } +} +'''; + +const String prBaseEditedEvent = ''' +{ + "action": "edited", + "number": 47609, + "pull_request": { + "url": "https://api.github.com/repos/flutter/engine/pulls/47609", + "id": 1584723957, + "node_id": "PR_kwDOAlZRSc5edPf1", + "html_url": "https://github.com/flutter/engine/pull/47609", + "diff_url": "https://github.com/flutter/engine/pull/47609.diff", + "patch_url": "https://github.com/flutter/engine/pull/47609.patch", + "issue_url": "https://api.github.com/repos/flutter/engine/issues/47609", + "number": 47609, + "state": "open", + "locked": false, + "title": "Upgrade Android SDK to 34 UpsideDownCake", + "user": { + "login": "gmackall", + "id": 34871572, + "node_id": "MDQ6VXNlcjM0ODcxNTcy", + "avatar_url": "https://avatars.githubusercontent.com/u/34871572?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/gmackall", + "html_url": "https://github.com/gmackall", + "followers_url": "https://api.github.com/users/gmackall/followers", + "following_url": "https://api.github.com/users/gmackall/following{/other_user}", + "gists_url": "https://api.github.com/users/gmackall/gists{/gist_id}", + "starred_url": "https://api.github.com/users/gmackall/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/gmackall/subscriptions", + "organizations_url": "https://api.github.com/users/gmackall/orgs", + "repos_url": "https://api.github.com/users/gmackall/repos", + "events_url": "https://api.github.com/users/gmackall/events{/privacy}", + "received_events_url": "https://api.github.com/users/gmackall/received_events", + "type": "User", + "site_admin": false + }, + "body": "~**This should not land until https://github.com/flutter/buildroot/pull/790 (re)lands, and I swap the buildroot url back to the latest commit.**~ Reland of PR to update buildroot at https://github.com/flutter/buildroot/pull/792. Upgrades to android api 34 Also: 1. Upgrades to java 17 in DEPS/ci", + "created_at": "2023-11-02T17:09:59Z", + "updated_at": "2023-11-08T21:00:47Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": "8e5e3a59a5cba4239c542ed9a914899a246640b7", + "assignee": null, + "assignees": [ + + ], + "requested_reviewers": [ + + ], + "requested_teams": [ + + ], + "labels": [ + { + "id": 246348935, + "node_id": "MDU6TGFiZWwyNDYzNDg5MzU=", + "url": "https://api.github.com/repos/flutter/engine/labels/platform-android", + "name": "platform-android", + "color": "A4C639", + "default": false, + "description": null + } + ], + "milestone": null, + "draft": false, + "commits_url": "https://api.github.com/repos/flutter/engine/pulls/47609/commits", + "review_comments_url": "https://api.github.com/repos/flutter/engine/pulls/47609/comments", + "review_comment_url": "https://api.github.com/repos/flutter/engine/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/flutter/engine/issues/47609/comments", + "statuses_url": "https://api.github.com/repos/flutter/engine/statuses/a6765b4c309aa082bbebade68e0c7ec308a1cc6c", + "head": { + "label": "gmackall:upgrade_to_android14", + "ref": "upgrade_to_android14", + "sha": "a6765b4c309aa082bbebade68e0c7ec308a1cc6c", + "user": { + "login": "gmackall", + "id": 34871572, + "node_id": "MDQ6VXNlcjM0ODcxNTcy", + "avatar_url": "https://avatars.githubusercontent.com/u/34871572?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/gmackall", + "html_url": "https://github.com/gmackall", + "followers_url": "https://api.github.com/users/gmackall/followers", + "following_url": "https://api.github.com/users/gmackall/following{/other_user}", + "gists_url": "https://api.github.com/users/gmackall/gists{/gist_id}", + "starred_url": "https://api.github.com/users/gmackall/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/gmackall/subscriptions", + "organizations_url": "https://api.github.com/users/gmackall/orgs", + "repos_url": "https://api.github.com/users/gmackall/repos", + "events_url": "https://api.github.com/users/gmackall/events{/privacy}", + "received_events_url": "https://api.github.com/users/gmackall/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 547558963, + "node_id": "R_kgDOIKMWMw", + "name": "engine", + "full_name": "gmackall/engine", + "private": false, + "owner": { + "login": "gmackall", + "id": 34871572, + "node_id": "MDQ6VXNlcjM0ODcxNTcy", + "avatar_url": "https://avatars.githubusercontent.com/u/34871572?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/gmackall", + "html_url": "https://github.com/gmackall", + "followers_url": "https://api.github.com/users/gmackall/followers", + "following_url": "https://api.github.com/users/gmackall/following{/other_user}", + "gists_url": "https://api.github.com/users/gmackall/gists{/gist_id}", + "starred_url": "https://api.github.com/users/gmackall/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/gmackall/subscriptions", + "organizations_url": "https://api.github.com/users/gmackall/orgs", + "repos_url": "https://api.github.com/users/gmackall/repos", + "events_url": "https://api.github.com/users/gmackall/events{/privacy}", + "received_events_url": "https://api.github.com/users/gmackall/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/gmackall/engine", + "description": "The Flutter engine", + "fork": true, + "url": "https://api.github.com/repos/gmackall/engine", + "forks_url": "https://api.github.com/repos/gmackall/engine/forks", + "keys_url": "https://api.github.com/repos/gmackall/engine/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/gmackall/engine/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/gmackall/engine/teams", + "hooks_url": "https://api.github.com/repos/gmackall/engine/hooks", + "issue_events_url": "https://api.github.com/repos/gmackall/engine/issues/events{/number}", + "events_url": "https://api.github.com/repos/gmackall/engine/events", + "assignees_url": "https://api.github.com/repos/gmackall/engine/assignees{/user}", + "branches_url": "https://api.github.com/repos/gmackall/engine/branches{/branch}", + "tags_url": "https://api.github.com/repos/gmackall/engine/tags", + "blobs_url": "https://api.github.com/repos/gmackall/engine/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/gmackall/engine/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/gmackall/engine/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/gmackall/engine/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/gmackall/engine/statuses/{sha}", + "languages_url": "https://api.github.com/repos/gmackall/engine/languages", + "stargazers_url": "https://api.github.com/repos/gmackall/engine/stargazers", + "contributors_url": "https://api.github.com/repos/gmackall/engine/contributors", + "subscribers_url": "https://api.github.com/repos/gmackall/engine/subscribers", + "subscription_url": "https://api.github.com/repos/gmackall/engine/subscription", + "commits_url": "https://api.github.com/repos/gmackall/engine/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/gmackall/engine/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/gmackall/engine/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/gmackall/engine/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/gmackall/engine/contents/{+path}", + "compare_url": "https://api.github.com/repos/gmackall/engine/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/gmackall/engine/merges", + "archive_url": "https://api.github.com/repos/gmackall/engine/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/gmackall/engine/downloads", + "issues_url": "https://api.github.com/repos/gmackall/engine/issues{/number}", + "pulls_url": "https://api.github.com/repos/gmackall/engine/pulls{/number}", + "milestones_url": "https://api.github.com/repos/gmackall/engine/milestones{/number}", + "notifications_url": "https://api.github.com/repos/gmackall/engine/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/gmackall/engine/labels{/name}", + "releases_url": "https://api.github.com/repos/gmackall/engine/releases{/id}", + "deployments_url": "https://api.github.com/repos/gmackall/engine/deployments", + "created_at": "2022-10-07T22:25:57Z", + "updated_at": "2023-02-02T18:38:07Z", + "pushed_at": "2023-11-08T20:57:02Z", + "git_url": "git://github.com/gmackall/engine.git", + "ssh_url": "git@github.com:gmackall/engine.git", + "clone_url": "https://github.com/gmackall/engine.git", + "svn_url": "https://github.com/gmackall/engine", + "homepage": "https://flutter.dev", + "size": 466778, + "stargazers_count": 0, + "watchers_count": 0, + "language": "C++", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "has_discussions": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause New or Revised License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + + ], + "visibility": "public", + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main", + "allow_squash_merge": true, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": false, + "allow_update_branch": false, + "use_squash_pr_title_as_default": false, + "squash_merge_commit_message": "COMMIT_MESSAGES", + "squash_merge_commit_title": "COMMIT_OR_PR_TITLE", + "merge_commit_message": "PR_TITLE", + "merge_commit_title": "MERGE_MESSAGE" + } + }, + "base": { + "label": "flutter:main", + "ref": "main", + "sha": "941e246d4851f652cf13312180174ebc9395fac4", + "user": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/flutter", + "html_url": "https://github.com/flutter", + "followers_url": "https://api.github.com/users/flutter/followers", + "following_url": "https://api.github.com/users/flutter/following{/other_user}", + "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", + "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", + "organizations_url": "https://api.github.com/users/flutter/orgs", + "repos_url": "https://api.github.com/users/flutter/repos", + "events_url": "https://api.github.com/users/flutter/events{/privacy}", + "received_events_url": "https://api.github.com/users/flutter/received_events", + "type": "Organization", + "site_admin": false + }, + "repo": { + "id": 39211337, + "node_id": "MDEwOlJlcG9zaXRvcnkzOTIxMTMzNw==", + "name": "engine", + "full_name": "flutter/engine", + "private": false, + "owner": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/flutter", + "html_url": "https://github.com/flutter", + "followers_url": "https://api.github.com/users/flutter/followers", + "following_url": "https://api.github.com/users/flutter/following{/other_user}", + "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", + "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", + "organizations_url": "https://api.github.com/users/flutter/orgs", + "repos_url": "https://api.github.com/users/flutter/repos", + "events_url": "https://api.github.com/users/flutter/events{/privacy}", + "received_events_url": "https://api.github.com/users/flutter/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/flutter/engine", + "description": "The Flutter engine", + "fork": false, + "url": "https://api.github.com/repos/flutter/engine", + "forks_url": "https://api.github.com/repos/flutter/engine/forks", + "keys_url": "https://api.github.com/repos/flutter/engine/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/flutter/engine/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/flutter/engine/teams", + "hooks_url": "https://api.github.com/repos/flutter/engine/hooks", + "issue_events_url": "https://api.github.com/repos/flutter/engine/issues/events{/number}", + "events_url": "https://api.github.com/repos/flutter/engine/events", + "assignees_url": "https://api.github.com/repos/flutter/engine/assignees{/user}", + "branches_url": "https://api.github.com/repos/flutter/engine/branches{/branch}", + "tags_url": "https://api.github.com/repos/flutter/engine/tags", + "blobs_url": "https://api.github.com/repos/flutter/engine/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/flutter/engine/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/flutter/engine/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/flutter/engine/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/flutter/engine/statuses/{sha}", + "languages_url": "https://api.github.com/repos/flutter/engine/languages", + "stargazers_url": "https://api.github.com/repos/flutter/engine/stargazers", + "contributors_url": "https://api.github.com/repos/flutter/engine/contributors", + "subscribers_url": "https://api.github.com/repos/flutter/engine/subscribers", + "subscription_url": "https://api.github.com/repos/flutter/engine/subscription", + "commits_url": "https://api.github.com/repos/flutter/engine/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/flutter/engine/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/flutter/engine/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/flutter/engine/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/flutter/engine/contents/{+path}", + "compare_url": "https://api.github.com/repos/flutter/engine/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/flutter/engine/merges", + "archive_url": "https://api.github.com/repos/flutter/engine/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/flutter/engine/downloads", + "issues_url": "https://api.github.com/repos/flutter/engine/issues{/number}", + "pulls_url": "https://api.github.com/repos/flutter/engine/pulls{/number}", + "milestones_url": "https://api.github.com/repos/flutter/engine/milestones{/number}", + "notifications_url": "https://api.github.com/repos/flutter/engine/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/flutter/engine/labels{/name}", + "releases_url": "https://api.github.com/repos/flutter/engine/releases{/id}", + "deployments_url": "https://api.github.com/repos/flutter/engine/deployments", + "created_at": "2015-07-16T17:39:56Z", + "updated_at": "2023-11-08T07:16:25Z", + "pushed_at": "2023-11-08T21:00:38Z", + "git_url": "git://github.com/flutter/engine.git", + "ssh_url": "git@github.com:flutter/engine.git", + "clone_url": "https://github.com/flutter/engine.git", + "svn_url": "https://github.com/flutter/engine", + "homepage": "https://flutter.dev", + "size": 704170, + "stargazers_count": 6848, + "watchers_count": 6848, + "language": "C++", + "has_issues": false, + "has_projects": false, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "has_discussions": false, + "forks_count": 5600, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 99, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause New or Revised License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + "c-plus-plus" + ], + "visibility": "public", + "forks": 5600, + "open_issues": 99, + "watchers": 6848, + "default_branch": "main", + "allow_squash_merge": true, + "allow_merge_commit": false, + "allow_rebase_merge": false, + "allow_auto_merge": false, + "delete_branch_on_merge": true, + "allow_update_branch": true, + "use_squash_pr_title_as_default": true, + "squash_merge_commit_message": "PR_BODY", + "squash_merge_commit_title": "PR_TITLE", + "merge_commit_message": "PR_TITLE", + "merge_commit_title": "MERGE_MESSAGE" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/flutter/engine/pulls/47609" + }, + "html": { + "href": "https://github.com/flutter/engine/pull/47609" + }, + "issue": { + "href": "https://api.github.com/repos/flutter/engine/issues/47609" + }, + "comments": { + "href": "https://api.github.com/repos/flutter/engine/issues/47609/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/flutter/engine/pulls/47609/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/flutter/engine/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/flutter/engine/pulls/47609/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/flutter/engine/statuses/a6765b4c309aa082bbebade68e0c7ec308a1cc6c" + } + }, + "author_association": "MEMBER", + "auto_merge": null, + "active_lock_reason": null, + "merged": false, + "mergeable": true, + "rebaseable": true, + "mergeable_state": "clean", + "merged_by": null, + "comments": 4, + "review_comments": 4, + "maintainer_can_modify": true, + "commits": 32, + "additions": 83, + "deletions": 77, + "changed_files": 18 + }, + "changes": { + "base": { + "ref": { + "from": "main" + }, + "sha": { + "from": "b3af5d64d3e6e2110b07d71909fc432537339659" + } + } + }, + "repository": { + "id": 39211337, + "node_id": "MDEwOlJlcG9zaXRvcnkzOTIxMTMzNw==", + "name": "engine", + "full_name": "flutter/engine", + "private": false, + "owner": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/flutter", + "html_url": "https://github.com/flutter", + "followers_url": "https://api.github.com/users/flutter/followers", + "following_url": "https://api.github.com/users/flutter/following{/other_user}", + "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", + "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", + "organizations_url": "https://api.github.com/users/flutter/orgs", + "repos_url": "https://api.github.com/users/flutter/repos", + "events_url": "https://api.github.com/users/flutter/events{/privacy}", + "received_events_url": "https://api.github.com/users/flutter/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/flutter/engine", + "description": "The Flutter engine", + "fork": false, + "url": "https://api.github.com/repos/flutter/engine", + "forks_url": "https://api.github.com/repos/flutter/engine/forks", + "keys_url": "https://api.github.com/repos/flutter/engine/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/flutter/engine/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/flutter/engine/teams", + "hooks_url": "https://api.github.com/repos/flutter/engine/hooks", + "issue_events_url": "https://api.github.com/repos/flutter/engine/issues/events{/number}", + "events_url": "https://api.github.com/repos/flutter/engine/events", + "assignees_url": "https://api.github.com/repos/flutter/engine/assignees{/user}", + "branches_url": "https://api.github.com/repos/flutter/engine/branches{/branch}", + "tags_url": "https://api.github.com/repos/flutter/engine/tags", + "blobs_url": "https://api.github.com/repos/flutter/engine/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/flutter/engine/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/flutter/engine/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/flutter/engine/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/flutter/engine/statuses/{sha}", + "languages_url": "https://api.github.com/repos/flutter/engine/languages", + "stargazers_url": "https://api.github.com/repos/flutter/engine/stargazers", + "contributors_url": "https://api.github.com/repos/flutter/engine/contributors", + "subscribers_url": "https://api.github.com/repos/flutter/engine/subscribers", + "subscription_url": "https://api.github.com/repos/flutter/engine/subscription", + "commits_url": "https://api.github.com/repos/flutter/engine/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/flutter/engine/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/flutter/engine/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/flutter/engine/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/flutter/engine/contents/{+path}", + "compare_url": "https://api.github.com/repos/flutter/engine/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/flutter/engine/merges", + "archive_url": "https://api.github.com/repos/flutter/engine/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/flutter/engine/downloads", + "issues_url": "https://api.github.com/repos/flutter/engine/issues{/number}", + "pulls_url": "https://api.github.com/repos/flutter/engine/pulls{/number}", + "milestones_url": "https://api.github.com/repos/flutter/engine/milestones{/number}", + "notifications_url": "https://api.github.com/repos/flutter/engine/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/flutter/engine/labels{/name}", + "releases_url": "https://api.github.com/repos/flutter/engine/releases{/id}", + "deployments_url": "https://api.github.com/repos/flutter/engine/deployments", + "created_at": "2015-07-16T17:39:56Z", + "updated_at": "2023-11-08T07:16:25Z", + "pushed_at": "2023-11-08T21:00:38Z", + "git_url": "git://github.com/flutter/engine.git", + "ssh_url": "git@github.com:flutter/engine.git", + "clone_url": "https://github.com/flutter/engine.git", + "svn_url": "https://github.com/flutter/engine", + "homepage": "https://flutter.dev", + "size": 704170, + "stargazers_count": 6848, + "watchers_count": 6848, + "language": "C++", + "has_issues": false, + "has_projects": false, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "has_discussions": false, + "forks_count": 5600, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 99, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause New or Revised License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + "c-plus-plus" + ], + "visibility": "public", + "forks": 5600, + "open_issues": 99, + "watchers": 6848, + "default_branch": "main", + "custom_properties": { + + } + }, + "organization": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "url": "https://api.github.com/orgs/flutter", + "repos_url": "https://api.github.com/orgs/flutter/repos", + "events_url": "https://api.github.com/orgs/flutter/events", + "hooks_url": "https://api.github.com/orgs/flutter/hooks", + "issues_url": "https://api.github.com/orgs/flutter/issues", + "members_url": "https://api.github.com/orgs/flutter/members{/member}", + "public_members_url": "https://api.github.com/orgs/flutter/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "description": "Flutter is Google's UI toolkit for building beautiful, natively compiled applications for mobile, web, desktop, and embedded devices from a single codebase." + }, + "enterprise": { + "id": 1732, + "slug": "alphabet", + "name": "Alphabet", + "node_id": "MDEwOkVudGVycHJpc2UxNzMy", + "avatar_url": "https://avatars.githubusercontent.com/b/1732?v=4", + "description": "", + "website_url": "https://abc.xyz/", + "html_url": "https://github.com/enterprises/alphabet", + "created_at": "2019-12-19T00:30:52Z", + "updated_at": "2023-01-20T00:41:48Z" + }, + "sender": { + "login": "gmackall", + "id": 34871572, + "node_id": "MDQ6VXNlcjM0ODcxNTcy", + "avatar_url": "https://avatars.githubusercontent.com/u/34871572?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/gmackall", + "html_url": "https://github.com/gmackall", + "followers_url": "https://api.github.com/users/gmackall/followers", + "following_url": "https://api.github.com/users/gmackall/following{/other_user}", + "gists_url": "https://api.github.com/users/gmackall/gists{/gist_id}", + "starred_url": "https://api.github.com/users/gmackall/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/gmackall/subscriptions", + "organizations_url": "https://api.github.com/users/gmackall/orgs", + "repos_url": "https://api.github.com/users/gmackall/repos", + "events_url": "https://api.github.com/users/gmackall/events{/privacy}", + "received_events_url": "https://api.github.com/users/gmackall/received_events", + "type": "User", + "site_admin": false + }, + "installation": { + "id": 10381585, + "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMTAzODE1ODU=" + } +} +'''; diff --git a/test/unit/checks_test.dart b/test/unit/checks_test.dart new file mode 100644 index 00000000..2eb7e6c4 --- /dev/null +++ b/test/unit/checks_test.dart @@ -0,0 +1,234 @@ +import 'dart:convert'; + +import 'package:github/src/common/model/checks.dart'; +import 'package:test/test.dart'; + +/// The checkRun Json is the official Github values +/// +/// Github api url: https://docs.github.com/en/rest/reference/checks#get-a-check-run +const checkRunJson = '''{ + "id": 4, + "head_sha": "ce587453ced02b1526dfb4cb910479d431683101", + "node_id": "MDg6Q2hlY2tSdW40", + "external_id": "", + "url": "https://api.github.com/repos/github/hello-world/check-runs/4", + "html_url": "https://github.com/github/hello-world/runs/4", + "details_url": "https://example.com", + "status": "completed", + "conclusion": "neutral", + "started_at": "2018-05-04T01:14:52Z", + "completed_at": "2018-05-04T01:14:52Z", + "output": { + "title": "Mighty Readme report", + "summary": "There are 0 failures, 2 warnings, and 1 notice.", + "text": "You may have some misspelled words on lines 2 and 4. You also may want to add a section in your README about how to install your app.", + "annotations_count": 2, + "annotations_url": "https://api.github.com/repos/github/hello-world/check-runs/4/annotations" + }, + "name": "mighty_readme", + "check_suite": { + "id": 5 + }, + "app": { + "id": 1, + "slug": "octoapp", + "node_id": "MDExOkludGVncmF0aW9uMQ==", + "owner": { + "login": "github", + "id": 1, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE=", + "url": "https://api.github.com/orgs/github", + "repos_url": "https://api.github.com/orgs/github/repos", + "events_url": "https://api.github.com/orgs/github/events", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": true + }, + "name": "Octocat App", + "description": "", + "external_url": "https://example.com", + "html_url": "https://github.com/apps/octoapp", + "created_at": "2017-07-08T16:18:44-04:00", + "updated_at": "2017-07-08T16:18:44-04:00", + "permissions": { + "metadata": "read", + "contents": "read", + "issues": "write", + "single_file": "write" + }, + "events": [ + "push", + "pull_request" + ] + }, + "pull_requests": [ + { + "url": "https://api.github.com/repos/github/hello-world/pulls/1", + "id": 1934, + "number": 3956, + "head": { + "ref": "say-hello", + "sha": "3dca65fa3e8d4b3da3f3d056c59aee1c50f41390", + "repo": { + "id": 526, + "url": "https://api.github.com/repos/github/hello-world", + "name": "hello-world" + } + }, + "base": { + "ref": "master", + "sha": "e7fdf7640066d71ad16a86fbcbb9c6a10a18af4f", + "repo": { + "id": 526, + "url": "https://api.github.com/repos/github/hello-world", + "name": "hello-world" + } + } + } + ] +}'''; + +const String expectedToString = + '{"name":"mighty_readme","id":4,"external_id":"","status":"completed","head_sha":"","check_suite":{"id":5},"details_url":"https://example.com","started_at":"2018-05-04T01:14:52.000Z","conclusion":"neutral"}'; + +const String newCheckRun = + '{"name":"New CheckRun","id":12345,"external_id":"","status":"queued","head_sha":"","check_suite":{"id":123456},"details_url":"https://example.com","started_at":"2024-12-05T01:05:24.000Z","conclusion":"null"}'; + +void main() { + group('Check run', () { + test('CheckRun fromJson', () { + final checkRun = CheckRun.fromJson(jsonDecode(checkRunJson)); + + expect(checkRun.id, 4); + expect(checkRun.name, 'mighty_readme'); + expect(checkRun.conclusion, CheckRunConclusion.neutral); + }); + + test('CheckRun from freshly created and encoded', () { + final checkRun = CheckRun.fromJson(jsonDecode(newCheckRun)); + + expect(checkRun.id, 12345); + expect(checkRun.name, 'New CheckRun'); + expect(checkRun.conclusion, CheckRunConclusion.empty); + }); + + test('CheckRun fromJson for skipped conclusion', () { + /// The checkRun Json is the official Github values + /// + /// Github api url: https://docs.github.com/en/rest/reference/checks#get-a-check-run + const checkRunJson = '''{ + "id": 10, + "head_sha": "ce587453ced02b1526dfb4cb910479d431683101", + "node_id": "MDg6Q2hlY2tSdW40", + "external_id": "", + "url": "https://api.github.com/repos/github/hello-world/check-runs/4", + "html_url": "https://github.com/github/hello-world/runs/4", + "details_url": "https://example.com", + "status": "completed", + "conclusion": "skipped", + "started_at": "2018-05-04T01:14:52Z", + "completed_at": "2018-05-04T01:14:52Z", + "output": { + "title": "Mighty Readme report", + "summary": "There are 0 failures, 2 warnings, and 1 notice.", + "text": "You may have some misspelled words on lines 2 and 4. You also may want to add a section in your README about how to install your app.", + "annotations_count": 2, + "annotations_url": "https://api.github.com/repos/github/hello-world/check-runs/4/annotations" + }, + "name": "mighty_readme", + "check_suite": { + "id": 5 + }, + "app": { + "id": 1, + "slug": "octoapp", + "node_id": "MDExOkludGVncmF0aW9uMQ==", + "owner": { + "login": "github", + "id": 1, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE=", + "url": "https://api.github.com/orgs/github", + "repos_url": "https://api.github.com/orgs/github/repos", + "events_url": "https://api.github.com/orgs/github/events", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": true + }, + "name": "Octocat App", + "description": "", + "external_url": "https://example.com", + "html_url": "https://github.com/apps/octoapp", + "created_at": "2017-07-08T16:18:44-04:00", + "updated_at": "2017-07-08T16:18:44-04:00", + "permissions": { + "metadata": "read", + "contents": "read", + "issues": "write", + "single_file": "write" + }, + "events": [ + "push", + "pull_request" + ] + }, + "pull_requests": [ + { + "url": "https://api.github.com/repos/github/hello-world/pulls/1", + "id": 1934, + "number": 3956, + "head": { + "ref": "say-hello", + "sha": "3dca65fa3e8d4b3da3f3d056c59aee1c50f41390", + "repo": { + "id": 526, + "url": "https://api.github.com/repos/github/hello-world", + "name": "hello-world" + } + }, + "base": { + "ref": "master", + "sha": "e7fdf7640066d71ad16a86fbcbb9c6a10a18af4f", + "repo": { + "id": 526, + "url": "https://api.github.com/repos/github/hello-world", + "name": "hello-world" + } + } + } + ] + }'''; + final checkRun = CheckRun.fromJson(jsonDecode(checkRunJson)); + + expect(checkRun.id, 10); + expect(checkRun.name, 'mighty_readme'); + expect(checkRun.conclusion, CheckRunConclusion.skipped); + }); + + test('CheckRun toString', () { + // indirectly tests the toJson method as well. + final checkRun = CheckRun.fromJson(jsonDecode(checkRunJson)); + expect(checkRun, isNotNull); + final checkRunString = checkRun.toString(); + expect(checkRunString, isNotNull); + expect(checkRunString == expectedToString, isTrue); + }); + }); +} diff --git a/test/unit/checksuite_test.dart b/test/unit/checksuite_test.dart new file mode 100644 index 00000000..d9571bc9 --- /dev/null +++ b/test/unit/checksuite_test.dart @@ -0,0 +1,121 @@ +import 'dart:convert'; + +import 'package:github/src/common/model/checks.dart'; +import 'package:test/test.dart'; + +/// The checkSuite Json is composed from multiple GitHub examples +/// +/// See https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28 +/// See https://docs.github.com/en/rest/checks/suites?apiVersion=2022-11-28 +const checkSuiteJson = '''{ + "id": 5, + "head_branch": "main", + "head_sha": "d6fde92930d4715a2b49857d24b940956b26d2d3", + "conclusion": "neutral", + "pull_requests": [ + { + "url": "https://api.github.com/repos/github/hello-world/pulls/1", + "id": 1934, + "number": 3956, + "head": { + "ref": "say-hello", + "sha": "3dca65fa3e8d4b3da3f3d056c59aee1c50f41390", + "repo": { + "id": 526, + "url": "https://api.github.com/repos/github/hello-world", + "name": "hello-world" + } + }, + "base": { + "ref": "master", + "sha": "e7fdf7640066d71ad16a86fbcbb9c6a10a18af4f", + "repo": { + "id": 526, + "url": "https://api.github.com/repos/github/hello-world", + "name": "hello-world" + } + } + } + ] +}'''; + +const String expectedToString = + '{"name":"mighty_readme","id":4,"external_id":"","status":"completed","head_sha":"","check_suite":{"id":5},"details_url":"https://example.com","started_at":"2018-05-04T01:14:52.000Z","conclusion":"neutral"}'; + +void main() { + group('Check suite', () { + test('CheckSuite fromJson', () { + final checkSuite = CheckSuite.fromJson(jsonDecode(checkSuiteJson)); + + expect(checkSuite.id, 5); + expect(checkSuite.headBranch, 'main'); + expect(checkSuite.headSha, 'd6fde92930d4715a2b49857d24b940956b26d2d3'); + expect(checkSuite.conclusion, CheckRunConclusion.neutral); + expect(checkSuite.pullRequests.isNotEmpty, true); + }); + + test('CheckSuite fromJson for skipped conclusion', () { + /// The checkSuite Json is composed from multiple GitHub examples + /// + /// See https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28 + /// See https://docs.github.com/en/rest/checks/suites?apiVersion=2022-11-28 + const checkSuiteJson = '''{ + "id": 10, + "head_branch": "master", + "head_sha": "ce587453ced02b1526dfb4cb910479d431683101", + "conclusion": "skipped", + "pull_requests": [ + { + "url": "https://api.github.com/repos/github/hello-world/pulls/1", + "id": 1934, + "number": 3956, + "head": { + "ref": "say-hello", + "sha": "3dca65fa3e8d4b3da3f3d056c59aee1c50f41390", + "repo": { + "id": 526, + "url": "https://api.github.com/repos/github/hello-world", + "name": "hello-world" + } + }, + "base": { + "ref": "master", + "sha": "e7fdf7640066d71ad16a86fbcbb9c6a10a18af4f", + "repo": { + "id": 526, + "url": "https://api.github.com/repos/github/hello-world", + "name": "hello-world" + } + } + } + ] + }'''; + final checkSuite = CheckSuite.fromJson(jsonDecode(checkSuiteJson)); + + expect(checkSuite.id, 10); + expect(checkSuite.headBranch, 'master'); + expect(checkSuite.headSha, 'ce587453ced02b1526dfb4cb910479d431683101'); + expect(checkSuite.conclusion, CheckRunConclusion.skipped); + expect(checkSuite.pullRequests.isNotEmpty, true); + }); + + test('CheckSuite fromJson for forked repository', () { + /// The checkSuite Json is composed from multiple GitHub examples + /// + /// See https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28 + /// See https://docs.github.com/en/rest/checks/suites?apiVersion=2022-11-28 + const checkSuiteJson = '''{ + "id": 10, + "head_branch": null, + "head_sha": "ce587453ced02b1526dfb4cb910479d431683101", + "conclusion": "skipped", + "pull_requests": [] + }'''; + final checkSuite = CheckSuite.fromJson(jsonDecode(checkSuiteJson)); + + expect(checkSuite.id, 10); + expect(checkSuite.headBranch, null); + expect(checkSuite.pullRequests.isEmpty, true); + }); + }); +} diff --git a/test/unit/common/model/misc_test.dart b/test/unit/common/model/misc_test.dart new file mode 100644 index 00000000..a7cbf3c2 --- /dev/null +++ b/test/unit/common/model/misc_test.dart @@ -0,0 +1,58 @@ +import 'dart:convert'; + +import 'package:github/src/common/model/misc.dart'; +import 'package:test/test.dart'; + +void main() { + group('RateLimit', () { + test('fromRateLimitResponse', () { + const rateLimitJson = ''' +{ + "resources": { + "core": { + "limit": 5000, + "remaining": 4999, + "reset": 1372700873, + "used": 1 + }, + "search": { + "limit": 30, + "remaining": 18, + "reset": 1372697452, + "used": 12 + }, + "graphql": { + "limit": 5000, + "remaining": 4993, + "reset": 1372700389, + "used": 7 + }, + "integration_manifest": { + "limit": 5000, + "remaining": 4999, + "reset": 1551806725, + "used": 1 + }, + "code_scanning_upload": { + "limit": 500, + "remaining": 499, + "reset": 1551806725, + "used": 1 + } + }, + "rate": { + "limit": 5000, + "remaining": 4999, + "reset": 1372700873, + "used": 1 + } +}'''; + final rateLimit = + RateLimit.fromRateLimitResponse(jsonDecode(rateLimitJson)); + + expect(rateLimit.limit, 5000); + expect(rateLimit.remaining, 4999); + expect(rateLimit.resets, DateTime.fromMillisecondsSinceEpoch(1372700873)); + }); + }); +} diff --git a/test/unit/common/model/pulls_test.dart b/test/unit/common/model/pulls_test.dart new file mode 100644 index 00000000..f4d5fe65 --- /dev/null +++ b/test/unit/common/model/pulls_test.dart @@ -0,0 +1,246 @@ +import 'dart:convert'; + +import 'package:github/src/common/model/pulls.dart'; +import 'package:test/test.dart'; + +const String samplePullRequest = ''' + { + "url": "https://api.github.com/repos/flutter/cocoon/pulls/2703", + "id": 1344460863, + "node_id": "PR_kwDOA8VHis5QItg_", + "html_url": "https://github.com/flutter/cocoon/pull/2703", + "diff_url": "https://github.com/flutter/cocoon/pull/2703.diff", + "patch_url": "https://github.com/flutter/cocoon/pull/2703.patch", + "issue_url": "https://api.github.com/repos/flutter/cocoon/issues/2703", + "number": 2703, + "state": "open", + "locked": false, + "title": "Bump url_launcher from 6.1.10 to 6.1.11 in /dashboard", + "user": { + "login": "dependabot[bot]", + "id": 49699333, + "node_id": "MDM6Qm90NDk2OTkzMzM=", + "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/dependabot%5Bbot%5D", + "html_url": "https://github.com/apps/dependabot", + "type": "Bot", + "site_admin": false + }, + "body": "Bumps [url_launcher](https://github.com/flutter/packages/tree/main/packages/url_launcher) from 6.1.10 to 6.1.11.", + "created_at": "2023-05-09T22:23:34Z", + "updated_at": "2023-05-09T22:23:35Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": "252a1a4370e30631b090eeeda182879985cc8f08", + "assignee": null, + "assignees": [ + + ], + "requested_reviewers": [ + + ], + "requested_teams": [ + + ], + "labels": [ + { + "id": 3960015931, + "node_id": "LA_kwDOA8VHis7sCQw7", + "url": "https://api.github.com/repos/flutter/cocoon/labels/autosubmit", + "name": "autosubmit", + "color": "0E8A16", + "default": false, + "description": "Merge PR when tree becomes green via auto submit App" + } + ], + "milestone": null, + "draft": false, + "commits_url": "https://api.github.com/repos/flutter/cocoon/pulls/2703/commits", + "review_comments_url": "https://api.github.com/repos/flutter/cocoon/pulls/2703/comments", + "review_comment_url": "https://api.github.com/repos/flutter/cocoon/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/flutter/cocoon/issues/2703/comments", + "statuses_url": "https://api.github.com/repos/flutter/cocoon/statuses/57ec5a040c8a631e39b3f3dee82a77fdf79b6e19", + "head": { + "label": "flutter:dependabot/pub/dashboard/url_launcher-6.1.11", + "ref": "dependabot/pub/dashboard/url_launcher-6.1.11", + "sha": "57ec5a040c8a631e39b3f3dee82a77fdf79b6e19", + "user": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/flutter", + "html_url": "https://github.com/flutter", + "type": "Organization", + "site_admin": false + }, + "repo": { + "id": 63260554, + "node_id": "MDEwOlJlcG9zaXRvcnk2MzI2MDU1NA==", + "name": "cocoon", + "full_name": "flutter/cocoon", + "private": false, + "owner": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/flutter", + "html_url": "https://github.com/flutter", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/flutter/cocoon", + "description": "Flutter's build coordinator and aggregator", + "fork": false, + "url": "https://api.github.com/repos/flutter/cocoon", + "forks_url": "https://api.github.com/repos/flutter/cocoon/forks", + "created_at": "2016-07-13T16:04:04Z", + "updated_at": "2023-04-12T16:34:46Z", + "pushed_at": "2023-05-09T22:23:35Z", + "git_url": "git://github.com/flutter/cocoon.git", + "ssh_url": "git@github.com:flutter/cocoon.git", + "clone_url": "https://github.com/flutter/cocoon.git", + "svn_url": "https://github.com/flutter/cocoon", + "homepage": null, + "size": 13247, + "stargazers_count": 171, + "watchers_count": 171, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause New or Revised License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + + ], + "visibility": "public", + "forks": 91, + "open_issues": 2, + "watchers": 171, + "default_branch": "main", + "allow_squash_merge": true, + "allow_merge_commit": false, + "allow_rebase_merge": false, + "allow_auto_merge": false, + "delete_branch_on_merge": false, + "allow_update_branch": false, + "use_squash_pr_title_as_default": true, + "squash_merge_commit_message": "PR_BODY", + "squash_merge_commit_title": "PR_TITLE", + "merge_commit_message": "PR_TITLE", + "merge_commit_title": "MERGE_MESSAGE" + } + }, + "base": { + "label": "flutter:main", + "ref": "main", + "sha": "152dd99368b8417b2ede8ed49d5923e594a3b0f2", + "user": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/flutter", + "html_url": "https://github.com/flutter", + "type": "Organization", + "site_admin": false + }, + "repo": { + "id": 63260554, + "node_id": "MDEwOlJlcG9zaXRvcnk2MzI2MDU1NA==", + "name": "cocoon", + "full_name": "flutter/cocoon", + "private": false, + "owner": { + "login": "flutter", + "id": 14101776, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/flutter", + "html_url": "https://github.com/flutter", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/flutter/cocoon", + "description": "Flutter's build coordinator and aggregator", + "fork": false, + "url": "https://api.github.com/repos/flutter/cocoon", + "forks_url": "https://api.github.com/repos/flutter/cocoon/forks", + "created_at": "2016-07-13T16:04:04Z", + "updated_at": "2023-04-12T16:34:46Z", + "pushed_at": "2023-05-09T22:23:35Z", + "git_url": "git://github.com/flutter/cocoon.git", + "ssh_url": "git@github.com:flutter/cocoon.git", + "clone_url": "https://github.com/flutter/cocoon.git", + "svn_url": "https://github.com/flutter/cocoon", + "homepage": null, + "size": 13247, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause New or Revised License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + + ], + "visibility": "public", + "forks": 91, + "open_issues": 2, + "watchers": 171, + "default_branch": "main", + "allow_squash_merge": true, + "allow_merge_commit": false, + "allow_rebase_merge": false, + "allow_auto_merge": false, + "delete_branch_on_merge": false, + "allow_update_branch": false, + "use_squash_pr_title_as_default": true, + "squash_merge_commit_message": "PR_BODY", + "squash_merge_commit_title": "PR_TITLE", + "merge_commit_message": "PR_TITLE", + "merge_commit_title": "MERGE_MESSAGE" + } + }, + "author_association": "CONTRIBUTOR", + "auto_merge": null, + "active_lock_reason": null, + "merged": false, + "mergeable": true, + "rebaseable": true, + "mergeable_state": "unstable", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "maintainer_can_modify": false, + "commits": 1, + "additions": 119, + "deletions": 202, + "changed_files": 2 + } +'''; + +void main() { + group('Pull Request fromJson', () { + test('Node ID is collected', () { + final pullRequest = PullRequest.fromJson(jsonDecode(samplePullRequest)); + expect(pullRequest, isNotNull); + expect(pullRequest.nodeId, "PR_kwDOA8VHis5QItg_"); + }); + }); +} diff --git a/test/unit/issues_test.dart b/test/unit/issues_test.dart new file mode 100644 index 00000000..512d83a3 --- /dev/null +++ b/test/unit/issues_test.dart @@ -0,0 +1,63 @@ +import 'dart:convert'; +import 'package:github/src/common/model/issues.dart'; + +import 'package:test/test.dart'; + +const String testIssueCommentJson = ''' + { + "url": "https://api.github.com/repos/flutter/cocoon/issues/comments/1352355796", + "html_url": "https://github.com/flutter/cocoon/pull/2356#issuecomment-1352355796", + "issue_url": "https://api.github.com/repos/flutter/cocoon/issues/2356", + "id": 1352355796, + "node_id": "IC_kwDOA8VHis5Qm0_U", + "user": { + "login": "CaseyHillers", + "id": 2148558, + "node_id": "MDQ6VXNlcjIxNDg1NTg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2148558?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/CaseyHillers", + "html_url": "https://github.com/CaseyHillers", + "followers_url": "https://api.github.com/users/CaseyHillers/followers", + "following_url": "https://api.github.com/users/CaseyHillers/following{/other_user}", + "gists_url": "https://api.github.com/users/CaseyHillers/gists{/gist_id}", + "starred_url": "https://api.github.com/users/CaseyHillers/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/CaseyHillers/subscriptions", + "organizations_url": "https://api.github.com/users/CaseyHillers/orgs", + "repos_url": "https://api.github.com/users/CaseyHillers/repos", + "events_url": "https://api.github.com/users/CaseyHillers/events{/privacy}", + "received_events_url": "https://api.github.com/users/CaseyHillers/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2022-12-14T23:26:32Z", + "updated_at": "2022-12-14T23:26:32Z", + "author_association": "MEMBER", + "body": "FYI you need to run https://github.com/flutter/cocoon/blob/main/format.sh for formatting Cocoon code", + "reactions": { + "url": "https://api.github.com/repos/flutter/cocoon/issues/comments/1352355796/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + } +'''; + +void main() { + group('Issue Comments', () { + test('IssueComment from Json', () { + final issueComment = + IssueComment.fromJson(jsonDecode(testIssueCommentJson)); + expect(1352355796, issueComment.id); + expect('MEMBER', issueComment.authorAssociation); + expect('CaseyHillers', issueComment.user!.login); + }); + }); +} diff --git a/test/unit/orgs_service_test.dart b/test/unit/orgs_service_test.dart new file mode 100644 index 00000000..9ba4ff3f --- /dev/null +++ b/test/unit/orgs_service_test.dart @@ -0,0 +1,174 @@ +import 'dart:io'; + +import 'package:github/github.dart'; +import 'package:http/http.dart'; +import 'package:http/testing.dart'; +import 'package:test/test.dart'; + +void main() { + const teamResponse = ''' + { + "name": "flutter-hackers", + "id": 1753404, + "slug": "flutter-hackers", + "permission": "pull", + "members_count": 255, + "repos_count": 34, + "organization": { + "login": "flutter", + "id": 14101776, + "url": "https://api.github.com/orgs/flutter", + "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", + "name": "Flutter", + "company": null, + "blog": "https://flutter.dev", + "location": null, + "email": null, + "public_repos": 30, + "public_gists": 0, + "followers": 6642, + "following": 0, + "html_url": "https://github.com/flutter", + "created_at": "2015-09-03T00:37:37Z", + "updated_at": "2022-03-17T17:35:40Z" + } + } +'''; + + const teamNotFoundResponse = ''' + { + "message": "Not Found", + "documentation_url": "https://docs.github.com/rest/reference/teams#list-teams" + } + '''; + + const activeMemberResponse = ''' + { + "state": "active", + "role": "member", + "url": "https://api.github.com/organizations/14101776/team/1753404/memberships/ricardoamador" + } + '''; + + const pendingMemberResponse = ''' + { + "state": "pending", + "role": "member", + "url": "https://api.github.com/organizations/14101776/team/1753404/memberships/ricardoamador" + } + '''; + + group(GitHub, () { + test('getTeamByName is successful', () async { + Request? request; + + final client = MockClient((r) async { + request = r; + return Response(teamResponse, HttpStatus.ok); + }); + + final github = GitHub(client: client); + final organizationsService = OrganizationsService(github); + + final team = await organizationsService.getTeamByName( + 'flutter', 'flutter-hackers'); + expect(team.name, 'flutter-hackers'); + expect(team.id, 1753404); + expect(team.organization!.login, 'flutter'); + expect(request, isNotNull); + }); + + test('getTeamByName not found', () async { + Request? request; + + final headers = {}; + headers['content-type'] = 'application/json'; + + final client = MockClient((r) async { + request = r; + return Response(teamNotFoundResponse, HttpStatus.notFound, + headers: headers); + }); + + final github = GitHub(client: client); + final organizationsService = OrganizationsService(github); + + expect( + () async => organizationsService.getTeamByName( + 'flutter', 'flutter-programmers'), + throwsException); + expect(request, isNull); + }); + + test('getTeamMembership using string name, active', () async { + Request? request; + + final client = MockClient((r) async { + request = r; + return Response(activeMemberResponse, HttpStatus.ok); + }); + + final github = GitHub(client: client); + final organizationsService = OrganizationsService(github); + + final teamMembershipState = + await organizationsService.getTeamMembershipByName( + 'flutter', + 'flutter-hackers', + 'ricardoamador', + ); + expect(teamMembershipState.isActive, isTrue); + expect(request, isNotNull); + }); + + test('getTeamMembership using string name, pending', () async { + Request? request; + + final client = MockClient((r) async { + request = r; + return Response(pendingMemberResponse, HttpStatus.ok); + }); + + final github = GitHub(client: client); + final organizationsService = OrganizationsService(github); + + final teamMembershipState = + await organizationsService.getTeamMembershipByName( + 'flutter', + 'flutter-hackers', + 'ricardoamador', + ); + expect(teamMembershipState.isActive, isFalse); + expect(teamMembershipState.isPending, isTrue); + expect(request, isNotNull); + }); + + test('getTeamMembership using string name, null', () async { + Request? request; + + final headers = {}; + headers['content-type'] = 'application/json'; + + final client = MockClient((r) async { + request = r; + return Response( + teamNotFoundResponse, + HttpStatus.notFound, + headers: headers, + ); + }); + + final github = GitHub(client: client); + final organizationsService = OrganizationsService(github); + + expect( + () async => organizationsService.getTeamMembershipByName( + 'flutter', + 'flutter-hackers', + 'garfield', + ), + throwsException); + expect(request, isNull); + }); + }); +} diff --git a/tool/ci/retry.sh b/tool/ci/retry.sh deleted file mode 100755 index b784c8d8..00000000 --- a/tool/ci/retry.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -n=0 -LAST_EXIT=0 -until [ $n -ge 5 ] -do - echo "$ ${@}" - ${@} - LAST_EXIT=${?} - [ ${LAST_EXIT} == 0 ] && break - n=$[$n+1] - sleep 2 -done - -exit ${LAST_EXIT} \ No newline at end of file diff --git a/tool/language_color_generator.dart b/tool/language_color_generator.dart index 67afc165..2bc948f1 100644 --- a/tool/language_color_generator.dart +++ b/tool/language_color_generator.dart @@ -8,22 +8,23 @@ const _path = './lib/src/const/language_color.dart'; const _url = 'https://raw.githubusercontent.com/' 'github/linguist/master/lib/linguist/languages.yml'; -Future main() async { - final response = await http.Client().get(_url); +Future main() async { + final response = await http.Client().get(Uri.parse(_url)); final yaml = loadYaml(response.body) as YamlMap; + final stringBuffer = StringBuffer() ..writeln('// GENERATED CODE - DO NOT MODIFY BY HAND') ..writeln('// VERSION OF ${DateTime.now().toIso8601String()}') ..writeln() - ..writeln('const languagesColor = {'); + ..writeln('const languageColors = {'); final map = yaml.value as YamlMap; + final languages = map.keys.cast().toList(growable: false)..sort(); for (var language in languages) { - final color = - map[language]['color']?.toString()?.toUpperCase() ?? '#EDEDED'; + final color = map[language]['color']?.toString().toUpperCase() ?? '#EDEDED'; language = language.replaceAll("'", "\\'"); diff --git a/tool/process_github_schema.dart b/tool/process_github_schema.dart new file mode 100644 index 00000000..14994289 --- /dev/null +++ b/tool/process_github_schema.dart @@ -0,0 +1,621 @@ +import 'dart:convert'; +import 'dart:io'; + +const int width = 72; + +List wordWrap(String body) { + var result = []; + var start = 0; + for (var index = 0; index < body.length; index += 1) { + if ((index == body.length - 1) || + (body[index] == '\n') || + ((body[index] == ' ') && (index - start > width))) { + result.add(body.substring(start, index + 1).trimRight()); + start = index + 1; + } + } + assert(start == body.length); + return result; +} + +typedef GenTypeVisitor = void Function(GenType type); + +abstract class GenType implements Comparable { + GenType(); + + String get name; + String get comment => ''; + + String get signature; + + void cleanup() {} + + String generateDeclaration(); + + void visit(GenTypeVisitor visitor) { + visitor(this); + } + + GenType mergeWith(GenType other, GenAbstractClass? superclass) { + assert(signature == other.signature, + 'cannot merge types with different signatures'); + throw StateError( + 'not sure how to merge $runtimeType with ${other.runtimeType}'); + } + + @override + int compareTo(GenType other) { + return signature.compareTo(other.signature); + } + + @override + String toString() => '$runtimeType($name)'; +} + +class GenPrimitive extends GenType { + GenPrimitive(this.type, this.comment); + + @override + String get name => type.toString(); + + @override + String get signature => name; + + @override + String generateDeclaration() => ''; + + @override + final String comment; + + final Type type; + + @override + GenType mergeWith(GenType other, GenAbstractClass? superclass) { + assert(superclass == null); + if (other is GenPrimitive) { + assert(type == other.type); + if (comment != other.comment) { + return GenPrimitive( + type, + '$comment\n\n${other.comment}', + ); + } + return this; + } + return super.mergeWith(other, superclass); + } +} + +class GenUnion extends GenType { + GenUnion(this.subtypes); + + @override + String get name => 'Object'; + + @override + String get comment { + var result = StringBuffer(); + result.writeln('One of the following:'); + for (final subtype in subtypes) { + if (subtype.comment.isNotEmpty) { + result.writeln( + ' * [${subtype.name}]: ${subtype.comment.split('\n').first}'); + } else { + result.writeln(' * [${subtype.name}]'); + } + } + return result.toString(); + } + + @override + String get signature { + var subsignatures = + subtypes.map((GenType type) => type.signature).toList() + ..sort() + ..join(','); + return 'Union<$subsignatures>'; + } + + final List subtypes; + + @override + String generateDeclaration() => ''; + + @override + void visit(GenTypeVisitor visitor) { + super.visit(visitor); + for (final subtype in subtypes) { + subtype.visit(visitor); + } + } + + @override + GenType mergeWith(GenType other, GenAbstractClass? superclass) { + assert(superclass == null); + if (other is GenUnion) { + assert(subtypes.length == other.subtypes.length); + var subtypesA = subtypes..sort(); + var subtypesB = other.subtypes..sort(); + var subtypesC = []; + for (var index = 0; index < subtypesA.length; index += 1) { + subtypesC.add(subtypesA[index].mergeWith(subtypesB[index], null)); + } + return GenUnion(subtypesC); + } + return super.mergeWith(other, superclass); + } +} + +class GenList extends GenType { + GenList(this.members, this.comment); + + @override + String get name => 'List<${members.name}>'; + + @override + final String comment; + + final GenType members; + + @override + String get signature { + return 'List<${members.signature}>'; + } + + @override + String generateDeclaration() => ''; + + @override + void visit(GenTypeVisitor visitor) { + super.visit(visitor); + members.visit(visitor); + } + + @override + GenType mergeWith(GenType other, GenAbstractClass? superclass) { + assert(superclass == null); + if (other is GenList) { + var newComment = + comment != other.comment ? '$comment\n\n${other.comment}' : comment; + var newMembers = members.mergeWith(other.members, null); + return GenList(newMembers, newComment); + } + return super.mergeWith(other, superclass); + } +} + +class GenAbstractClass extends GenType { + GenAbstractClass(this.name, this.comment, {Map? properties}) + : properties = properties ?? {}; + + @override + final String name; + + @override + final String comment; + + final List subclasses = []; + final Map properties; + + @override + String get signature { + var propertySignatures = properties.keys + .map((String propertyName) => + '$propertyName:${properties[propertyName]!.signature}') + .toList() + ..sort() + ..join(','); + return 'abstract class $name { $propertySignatures }'; + } + + @override + void cleanup() { + if (subclasses.length > 1) { + var names = subclasses.first.properties.keys.toSet(); + properties: + for (final name in names) { + var signature = subclasses.first.properties[name]!.signature; + for (final subclass in subclasses.skip(1)) { + if (!subclass.properties.containsKey(name) || + subclass.properties[name]!.signature != signature) { + continue properties; + } + } + var property = subclasses.first.properties[name]!; + for (final subclass in subclasses.skip(1)) { + property = property.mergeWith(subclass.properties[name]!, null); + } + properties[name] = property; + for (final subclass in subclasses) { + subclass.properties.remove(name); + } + } + } + } + + @override + String generateDeclaration() { + var output = StringBuffer(); + if (comment.isNotEmpty) { + for (final line in wordWrap(comment)) { + output.writeln('/// $line'); + } + } + output.writeln('@JsonSerializable()'); + output.writeln('abstract class $name {'); + output.write(' $name('); + if (properties.isNotEmpty) { + output.writeln('{'); + for (final propertyName in properties.keys.toList()..sort()) { + output.writeln(' this.$propertyName,'); + } + output.write(' }'); + } + output.writeln(');'); + output.writeln(''); + var lastLineWasBlank = true; + for (final propertyName in properties.keys.toList()..sort()) { + if (properties[propertyName]!.comment.isNotEmpty) { + if (!lastLineWasBlank) { + output.writeln(''); + lastLineWasBlank = true; + } + for (final line in wordWrap(properties[propertyName]!.comment)) { + output.writeln(' /// $line'); + } + } else { + lastLineWasBlank = false; + } + output.writeln(' ${properties[propertyName]!.name}? $propertyName;'); + if (lastLineWasBlank) { + output.writeln(''); + lastLineWasBlank = true; + } + } + output.writeln('}'); + return output.toString(); + } + + @override + void visit(GenTypeVisitor visitor) { + super.visit(visitor); + for (final subclass in subclasses) { + subclass.visit(visitor); + } + } + + @override + GenType mergeWith(GenType other, GenAbstractClass? superclass) { + assert(superclass == null); + if (other is GenAbstractClass) { + assert(name == other.name); + assert(properties.length == other.properties.length); + var newComment = + comment != other.comment ? '$comment\n\n${other.comment}' : comment; + var newProperties = {}; + for (final propertyName in properties.keys) { + newProperties[propertyName] = properties[propertyName]! + .mergeWith(other.properties[propertyName]!, null); + } + var result = + GenAbstractClass(name, newComment, properties: newProperties); + var subclassesA = subclasses..sort(); + var subclassesB = other.subclasses..sort(); + for (var index = 0; index < subclassesA.length; index += 1) { + subclassesA[index].mergeWith(subclassesB[index], result); + } + assert(result.subclasses.length == subclasses.length); + assert(result.subclasses.length == other.subclasses.length); + return result; + } + return super.mergeWith(other, superclass); + } +} + +class GenClass extends GenType { + GenClass(this.name, this.comment, this.superclass, this.properties) { + if (superclass != null) { + superclass!.subclasses.add(this); + } + } + + @override + final String name; + + @override + final String comment; + + final GenAbstractClass? superclass; + final Map properties; + + @override + String get signature { + var propertySignatures = properties.keys + .map((String propertyName) => + '$propertyName:${properties[propertyName]!.signature}') + .toList() + ..sort() + ..join(','); + return 'class $name extends { ${superclass?.signature} } with { $propertySignatures }'; + } + + @override + String generateDeclaration() { + var output = StringBuffer(); + if (comment.isNotEmpty) { + for (final line in wordWrap(comment)) { + output.writeln('/// $line'); + } + } + output.writeln('@JsonSerializable()'); + output.write('class $name '); + if (superclass != null) { + output.write('extends ${superclass!.name} '); + } + output.writeln('{'); + output.writeln(' $name({'); + if (superclass != null) { + for (final propertyName in superclass!.properties.keys.toList()..sort()) { + output.writeln(' super.$propertyName,'); + } + } + for (final propertyName in properties.keys.toList()..sort()) { + output.writeln(' this.$propertyName,'); + } + output.writeln(' });'); + output.writeln(''); + var lastLineWasBlank = true; + for (final propertyName in properties.keys.toList()..sort()) { + if (properties[propertyName]!.comment.isNotEmpty) { + if (!lastLineWasBlank) { + output.writeln(''); + lastLineWasBlank = true; + } + for (final line in wordWrap(properties[propertyName]!.comment)) { + output.writeln(' /// $line'); + } + } else { + lastLineWasBlank = false; + } + output.writeln(' ${properties[propertyName]!.name}? $propertyName;'); + if (lastLineWasBlank) { + output.writeln(''); + lastLineWasBlank = true; + } + } + if (!lastLineWasBlank) { + output.writeln(''); + } + output + .writeln(' Map toJson() => _\$${name}ToJson(this);'); + output.writeln(''); + output.writeln(' factory $name.fromJson(Map input) =>'); + output.writeln(' _\$${name}FromJson(input);'); + output.writeln('}'); + return output.toString(); + } + + @override + void visit(GenTypeVisitor visitor) { + super.visit(visitor); + for (final property in properties.values) { + property.visit(visitor); + } + } + + @override + GenType mergeWith(GenType other, GenAbstractClass? superclass) { + assert((superclass == null) == (this.superclass == null)); + if (other is GenClass) { + assert((other.superclass == null) == (this.superclass == null)); + assert(name == other.name); + assert(properties.length == other.properties.length); + var newComment = + comment != other.comment ? '$comment\n\n${other.comment}' : comment; + var newProperties = {}; + for (final propertyName in properties.keys) { + newProperties[propertyName] = properties[propertyName]! + .mergeWith(other.properties[propertyName]!, null); + } + return GenClass(name, newComment, superclass, newProperties); + } + return super.mergeWith(other, superclass); + } +} + +void assure(bool condition, String Function() callback) { + if (!condition) { + print(callback()); + exit(1); + } +} + +String? camelCase(String? text, {bool uppercase = false}) { + if (text == null) { + return null; + } + var bits = text.split(RegExp('[- _]')); + var result = StringBuffer(); + for (final bit in bits) { + if (bit.isNotEmpty) { + if (result.isNotEmpty || uppercase) { + result.write(String.fromCharCode(bit.runes.first).toUpperCase()); + result.write(String.fromCharCodes(bit.runes.skip(1))); + } else { + result.write(bit); + } + } + } + return result.toString(); +} + +String buildComment(Map schema) { + var description = StringBuffer(); + if (schema['title'] != null) { + description.writeln(schema['title']); + } + if (schema['description'] != null && + schema['description'] != schema['title']) { + if (description.isNotEmpty) { + description.writeln(''); + } + description.writeln(schema['description']); + } + if (schema['format'] != null) { + if (description.isNotEmpty) { + description.writeln(''); + } + description.write('Format: '); + description.writeln(schema['format']); + } + if (schema['examples'] != null) { + assure(schema['examples'] is List, + () => 'examples should be a list, not as in $schema'); + for (final example in schema['examples'] as List) { + if (description.isNotEmpty) { + description.writeln(''); + } + description.writeln('Example: `$example`'); + } + } + return description.toString().trimRight(); +} + +GenType process(Map schema, {String? defaultName}) { + final comment = buildComment(schema); + String type; + if (schema['type'] is List) { + var types = schema['type'] as List; + if (types.length == 2) { + if (types[0] == 'null' && types[1] is String) { + type = types[1] as String; + } else if (types[1] == 'null' && types[0] is String) { + type = types[0] as String; + } else { + print('Arbitrary union types not supported: $types'); + exit(1); + } + } else { + print('Arbitrary union types not supported: $types'); + exit(1); + } + } else if (schema['type'] is String) { + type = schema['type'] as String; + } else { + var anyOf = schema['anyOf'] ?? schema['oneOf']; + if (anyOf != null) { + assure(comment.isEmpty, () => 'lost comment to anyOf/oneOf: $comment'); + assure( + anyOf is List, () => 'anyOf/oneOf key is not a JSON list'); + var subtypes = []; + for (final subtype in anyOf as List) { + assure(subtype is Map, + () => 'type in anyOf/oneOf is not a JSON object'); + subtypes.add(process(subtype as Map)); + } + if (subtypes.length == 2) { + if (subtypes[0] is GenPrimitive && + (subtypes[0] as GenPrimitive).type == Null) { + return subtypes[1]; + } + if (subtypes[1] is GenPrimitive && + (subtypes[1] as GenPrimitive).type == Null) { + return subtypes[0]; + } + } + return GenUnion(subtypes); + } + if (schema['type'] == null) { + print('missing type: $schema'); + exit(1); + } + print('unknown type ${schema['type']}'); + exit(1); + } + if (type == 'array') { + assure(schema['items'] is Map, + () => 'array items are not a JSON object'); + return GenList(process(schema['items'] as Map), comment); + } + if (type == 'object') { + var anyOf = schema['anyOf']; + if (anyOf != null) { + assure(anyOf is List, () => 'anyOf key is not a JSON list'); + var result = GenAbstractClass( + camelCase(schema['title'] as String?) ?? '##unnamed##', + comment, + ); + for (final subschema in anyOf as List) { + assure(subschema is Map, + () => 'anyOf value is not a JSON object'); + var subclass = processObject(subschema as Map, + superclass: result); + assert(result.subclasses.last == subclass); + } + return result; + } + return processObject(schema, defaultName: defaultName); + } + if (type == 'null') { + return GenPrimitive(Null, comment); + } + if (type == 'boolean') { + return GenPrimitive(bool, comment); + } + if (type == 'integer') { + return GenPrimitive(int, comment); + } + if (type == 'string') { + return GenPrimitive(String, comment); + } + print('unknown type $type'); + exit(1); +} + +GenClass processObject(Map schema, + {GenAbstractClass? superclass, String? comment, String? defaultName}) { + assert(schema['anyOf'] == null); + comment ??= buildComment(schema); + var properties = {}; + var propertiesData = schema['properties']; + assure(propertiesData is Map, + () => 'properties key is not a JSON map'); + for (final propertyName in (propertiesData as Map).keys) { + var propertyData = propertiesData[propertyName]; + assure(propertyData is Map, + () => 'property $propertyName is not a JSON object'); + properties[camelCase(propertyName)!] = process( + propertyData as Map, + defaultName: camelCase(propertyName, uppercase: true)); + } + return GenClass( + camelCase(schema['title'] as String?) ?? defaultName ?? '##unnamed##', + comment, + superclass, + properties, + ); +} + +void main(List arguments) { + if (arguments.length != 1) { + print( + 'Command must be run with one argument, the file name of the schema to process.'); + exit(1); + } + Object schema = json.decode(File(arguments.single).readAsStringSync()); + assure(schema is Map, () => 'schema is not a JSON object'); + var rootType = process(schema as Map); + rootType.visit((GenType type) { + type.cleanup(); + }); + var declarations = {}; + rootType.visit((GenType type) { + var declaration = type.generateDeclaration().trimRight(); + declarations.add(declaration); + }); + for (final declaration in declarations) { + print(declaration); + print(''); + } + print('// root type is: ${rootType.name}'); +} diff --git a/tool/publish.sh b/tool/publish.sh deleted file mode 100755 index 836d0ae3..00000000 --- a/tool/publish.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -# Publishes a GitHub.dart release -./tool/build.dart publish ${@} -VERSION=`grep 'version:' pubspec.yaml | sed 's/version: //'` -echo Releasing ${VERSION} -git add . -git tag v${VERSION} -git commit -m "v${VERSION}" -git push --tags origin master diff --git a/tool/serve.sh b/tool/serve.sh deleted file mode 100755 index 1b0f658c..00000000 --- a/tool/serve.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -pub serve example/ test/ --hostname 0.0.0.0 --port 8080 diff --git a/tool/update-demos.sh b/tool/update-demos.sh deleted file mode 100755 index 27462b73..00000000 --- a/tool/update-demos.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [ -z ${1} ] -then - echo "Usage: tool/update-demos.sh path/to/demos" - exit 1 -fi - -rm -rf build -rm -rf ${1} - -pub build example --mode=debug -cp -R build/example ${1} - -cd ${1} -git add . -git commit -m "Updating Demos" -git push pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy