diff --git a/.github/workflows/update-pubspec.yaml b/.github/workflows/update-version.yaml similarity index 90% rename from .github/workflows/update-pubspec.yaml rename to .github/workflows/update-version.yaml index 9677b26..ebf55f1 100644 --- a/.github/workflows/update-pubspec.yaml +++ b/.github/workflows/update-version.yaml @@ -1,4 +1,4 @@ -name: update-pubspec +name: update-version on: workflow_dispatch: @@ -38,9 +38,9 @@ jobs: uses: peter-evans/create-pull-request@v4 with: token: ${{ secrets.GITHUB_TOKEN }} - commit-message: auto format - title: Auto Format + commit-message: update version + title: Update version body: This was automatically generated by the [${{ github.workflow }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}). base: main - branch: update-pubspec + branch: update-version branch-suffix: short-commit-hash diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ba3361..c847d7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## 2.0.0 + +### Changed + +- Changed to use "Create a review for a pull request" API. + - https://docs.github.com/en/rest/pulls/reviews#create-a-review-for-a-pull-request +- Changed to use `dart analyze --format=machine` command. + +### Improved + +- Supported for Dart SDK 2.15.0. +- Reduced dependent packages. + ## 1.0.6 ### Fixed diff --git a/README.md b/README.md index 6d7427b..e9229b7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,11 @@ # Elixir -A command-line tool for commenting `dart analyze` results to GitHub PullRequest. +A command-line tool for commenting `dart analyze --format=machine $dir` results to GitHub PullRequest. + +## Required + +- Dart SDK version >=2.15.0 <3.0.0 ## Usage @@ -26,7 +30,7 @@ jobs: - uses: dart-lang/setup-dart@v1 # https://github.com/blendthink/elixir - - uses: blendthink/elixir@v1 + - uses: blendthink/elixir@v2 with: # (Optional) GITHUB_TOKEN or a `repo` scoped Personal Access Token (PAT). # Default: ${{ github.token }} @@ -39,25 +43,21 @@ jobs: ### CLI -This package uses Git and GitHub CLI internally. +This package uses Git internally. -If you are using GitHub Actions, you can skip steps 1 and 2. +If you are using GitHub Actions, you can skip steps 1. #### 1. Install Git https://git-scm.com/book/en/v2/Getting-Started-Installing-Git -#### 2. Install GitHub CLI - -https://github.com/cli/cli#installation - -#### 3. Active package +#### 2. Active package ```shell dart pub global activate elixir ``` -#### 4. Run package +#### 3. Run package ```shell cd {git-repository-path} diff --git a/analysis_options.yaml b/analysis_options.yaml index 62a8984..8ae8dc2 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,5 +1,191 @@ -include: package:lints/recommended.yaml - analyzer: - errors: - invalid_annotation_target: ignore + exclude: + - example/** + strong-mode: + implicit-casts: false + language: + strict-inference: true + strict-raw-types: true + +linter: + rules: + # Error Rules + - always_use_package_imports + - avoid_dynamic_calls + - avoid_empty_else + - avoid_relative_lib_imports + - avoid_slow_async_io + - avoid_type_to_string + - avoid_types_as_parameter_names + - avoid_web_libraries_in_flutter + - cancel_subscriptions + - close_sinks + - comment_references + - control_flow_in_finally + - empty_statements + - hash_and_equals + - iterable_contains_unrelated_type + - list_remove_unrelated_type + - literal_only_boolean_expressions + - no_adjacent_strings_in_list + - no_duplicate_case_values + - no_logic_in_create_state + - prefer_void_to_null + - test_types_in_equals + - throw_in_finally + - unnecessary_statements + - unrelated_type_equality_checks + - unsafe_html + - use_build_context_synchronously + - use_key_in_widget_constructors + - valid_regexps + + # Style Rules + - always_declare_return_types + - always_require_non_null_named_parameters + - annotate_overrides + - avoid_annotating_with_dynamic + - avoid_bool_literals_in_conditional_expressions + - avoid_catches_without_on_clauses + - avoid_catching_errors + - avoid_classes_with_only_static_members + - avoid_double_and_int_checks + - avoid_escaping_inner_quotes + - avoid_field_initializers_in_const_classes + - avoid_function_literals_in_foreach_calls + - avoid_implementing_value_types + - avoid_init_to_null + - avoid_js_rounded_ints + - avoid_multiple_declarations_per_line + - avoid_null_checks_in_equality_operators + - avoid_equals_and_hash_code_on_mutable_classes + - avoid_positional_boolean_parameters + - avoid_private_typedef_functions + - avoid_redundant_argument_values + - avoid_renaming_method_parameters + - avoid_returning_null_for_void + - avoid_returning_this + - avoid_return_types_on_setters + - avoid_setters_without_getters + - avoid_shadowing_type_parameters + - avoid_single_cascade_in_expression_statements + - avoid_types_on_closure_parameters + - avoid_unnecessary_containers + - avoid_unused_constructor_parameters + - avoid_void_async + - await_only_futures + - camel_case_extensions + - camel_case_types + - cascade_invocations + - cast_nullable_to_non_nullable + - constant_identifier_names + - curly_braces_in_flow_control_structures + - deprecated_consistency + - directives_ordering + - empty_catches + - empty_constructor_bodies + - eol_at_end_of_file + - exhaustive_cases + - file_names + - flutter_style_todos + - implementation_imports + - join_return_with_assignment + - leading_newlines_in_multiline_strings + - library_names + - library_prefixes + - library_private_types_in_public_api + - lines_longer_than_80_chars + - missing_whitespace_between_adjacent_strings + - no_default_cases + - non_constant_identifier_names + - noop_primitive_operations + - no_runtimeType_toString + - null_check_on_nullable_type_parameter + - null_closures + - omit_local_variable_types + - only_throw_errors + - overridden_fields + - package_api_docs + - package_prefixed_library_names + - parameter_assignments + - prefer_adjacent_string_concatenation + - prefer_asserts_in_initializer_lists + - prefer_collection_literals + - prefer_conditional_assignment + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_constructors_over_static_methods + - prefer_contains + - prefer_equal_for_default_values + - prefer_expression_function_bodies + - prefer_final_fields + - prefer_final_in_for_each + - prefer_final_locals + - prefer_foreach + - prefer_for_elements_to_map_fromIterable + - prefer_function_declarations_over_variables + - prefer_generic_function_type_aliases + - prefer_if_elements_to_conditional_expressions + - prefer_if_null_operators + - prefer_initializing_formals + - prefer_inlined_adds + - prefer_interpolation_to_compose_strings + - prefer_int_literals + - prefer_is_empty + - prefer_is_not_empty + - prefer_is_not_operator + - prefer_iterable_whereType + - prefer_mixin + - prefer_null_aware_operators + - prefer_null_aware_method_calls + - prefer_single_quotes + - prefer_spread_collections + - prefer_typing_uninitialized_variables + - provide_deprecation_message + #- public_member_api_docs + - recursive_getters + - require_trailing_commas + - sized_box_for_whitespace + - slash_for_doc_comments + - sort_child_properties_last + - sort_constructors_first + - sort_unnamed_constructors_first + - tighten_type_of_initializing_formals + - type_annotate_public_apis + - type_init_formals + - unawaited_futures + - unnecessary_await_in_return + - unnecessary_brace_in_string_interps + - unnecessary_const + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_null_in_if_null_operators + - unnecessary_getters_setters + - unnecessary_lambdas + - unnecessary_nullable_for_final_variable_declarations + - unnecessary_null_checks + - unnecessary_overrides + - unnecessary_parenthesis + - unnecessary_raw_strings + - unnecessary_string_escapes + - unnecessary_string_interpolations + - unnecessary_this + - use_full_hex_values_for_flutter_colors + - use_function_type_syntax_for_parameters + - use_is_even_rather_than_modulo + - use_late_for_private_fields_and_variables + - use_named_constants + - use_rethrow_when_possible + - use_raw_strings + - use_setters_to_change_properties + - use_string_buffers + - use_test_throws_matchers + - use_to_and_as_if_applicable + - void_checks + + # Pub Rules + - depend_on_referenced_packages + - package_names + - sort_pub_dependencies diff --git a/bin/elixir.dart b/bin/elixir.dart index d9335d0..4fbbc54 100644 --- a/bin/elixir.dart +++ b/bin/elixir.dart @@ -1,3 +1,3 @@ import 'package:elixir/elixir.dart' as elixir; -Future main(List args) async => await elixir.run(args); +Future main(List args) async => elixir.run(args); diff --git a/build.yaml b/build.yaml index ef9d82c..3258a37 100644 --- a/build.yaml +++ b/build.yaml @@ -9,10 +9,9 @@ targets: options: ignore_for_file: - non_constant_identifier_names - json_serializable|freezed: + mockito|mockBuilder: generate_for: include: - - "lib/data/model/*.dart" + - "test/**.dart" exclude: - - "lib/data/model/*.freezed.dart" - - "lib/data/model/*.g.dart" + - "test/**.mocks.dart" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 281755e..e7279d2 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -3,4 +3,4 @@ description: A example of Elixir. version: 1.0.0 publish_to: 'none' environment: - sdk: '>=2.17.5 <3.0.0' + sdk: '>=2.15.0 <3.0.0' diff --git a/lib/cli/command/run.dart b/lib/cli/command/run.dart index 906b444..5745c02 100644 --- a/lib/cli/command/run.dart +++ b/lib/cli/command/run.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:convert'; import 'package:args/args.dart' show ArgResults; import 'package:args/command_runner.dart'; @@ -10,30 +9,17 @@ import 'package:elixir/cli/option/head.dart'; import 'package:elixir/cli/option/num.dart'; import 'package:elixir/cli/option/repo.dart'; import 'package:elixir/usecase/comment_indicates.dart'; -import 'package:elixir/usecase/filter_indicates.dart'; +import 'package:elixir/usecase/delete_previous_comments.dart'; import 'package:elixir/usecase/get_indicates.dart'; import 'package:elixir/util/log.dart'; class RunCommand extends Command { - @override - String get name => 'run'; - - @override - String get description => - 'Run `dart analyze` and comment on the GitHub Pull Request.'; - - final _encoder = JsonEncoder.withIndent(' '); - - final GetIndicatesUseCase _getIndicates; - final FilterIndicatesUseCase _filterIndicates; - final CommentIndicatesUseCase _commentIndicates; - RunCommand({ GetIndicatesUseCase getIndicates = const GetIndicatesUseCase(), - FilterIndicatesUseCase filterIndicates = const FilterIndicatesUseCase(), - CommentIndicatesUseCase commentIndicates = const CommentIndicatesUseCase(), + required DeletePreviousCommentsUseCase deletePreviousComments, + required CommentIndicatesUseCase commentIndicates, }) : _getIndicates = getIndicates, - _filterIndicates = filterIndicates, + _deletePreviousComments = deletePreviousComments, _commentIndicates = commentIndicates { argParser.addOptions([ RepoOption(), @@ -44,6 +30,17 @@ class RunCommand extends Command { ]); } + @override + String get name => 'run'; + + @override + String get description => + 'Run `dart analyze` and comment on the GitHub Pull Request.'; + + final GetIndicatesUseCase _getIndicates; + final DeletePreviousCommentsUseCase _deletePreviousComments; + final CommentIndicatesUseCase _commentIndicates; + @override FutureOr? run() async { final repo = RepoOption.repo(argResults, usageException); @@ -58,25 +55,18 @@ class RunCommand extends Command { head: head, ); if (indicates.isEmpty) { - return; - } - - final filteredIndicates = await _filterIndicates( - repo: repo, - num: num, - indicates: indicates, - ); - if (filteredIndicates.isEmpty) { log.i('In this Pull Request ( #$num ), no issues found!'); return; } - final reviewComments = await _commentIndicates( + await _deletePreviousComments(repo: repo, num: num); + + final result = await _commentIndicates( repo: repo, num: num, - indicates: filteredIndicates, + indicates: indicates, ); - log.i(_encoder.convert(reviewComments)); + log.i(result); } @override diff --git a/lib/cli/extension.dart b/lib/cli/extension.dart index 569ba37..ffc0035 100644 --- a/lib/cli/extension.dart +++ b/lib/cli/extension.dart @@ -1,15 +1,19 @@ import 'package:args/args.dart' show ArgResults, ArgParser; - import 'package:elixir/cli/option.dart'; extension ArgResultsExt on ArgResults { String requireParam(String name) { final p = optionalParam(name); - if (p == null) throw ArgumentError(); + if (p == null) { + throw ArgumentError(); + } return p; } - String? optionalParam(String name) => this[name]; + String? optionalParam(String name) { + final param = this[name]; + return param is String ? param : null; + } } extension ArgParserExt on ArgParser { diff --git a/lib/cli/flag/version.dart b/lib/cli/flag/version.dart index a1fc103..8491cfe 100644 --- a/lib/cli/flag/version.dart +++ b/lib/cli/flag/version.dart @@ -2,8 +2,6 @@ import 'package:args/args.dart'; import 'package:elixir/cli/option.dart'; class VersionFlag extends Flag { - static const _name = 'version'; - VersionFlag() : super( _name, @@ -12,7 +10,7 @@ class VersionFlag extends Flag { negatable: false, ); - static bool enabled(ArgResults argResults) { - return argResults.wasParsed(_name); - } + static const _name = 'version'; + + static bool enabled(ArgResults argResults) => argResults.wasParsed(_name); } diff --git a/lib/cli/option.dart b/lib/cli/option.dart index 7a3b3d0..3867b51 100644 --- a/lib/cli/option.dart +++ b/lib/cli/option.dart @@ -18,7 +18,7 @@ abstract class Flag extends _BaseOption { null, null, defaultsTo, - callback == null ? null : (value) => callback(value as bool), + callback == null ? null : (value) => callback(value), OptionType.flag, negatable: negatable, hide: hide, @@ -56,21 +56,6 @@ abstract class Option extends _BaseOption { } abstract class _BaseOption { - final String name; - final String? abbr; - final String? help; - final String? valueHelp; - final Iterable? allowed; - final Map? allowedHelp; - final T? defaultsTo; - final Function(T?)? callback; - final OptionType type; - final bool negatable; - final bool? splitCommas; - final bool mandatory; - final bool hide; - final List aliases; - _BaseOption( this.name, this.abbr, @@ -87,4 +72,19 @@ abstract class _BaseOption { this.hide = false, this.aliases = const [], }); + + final String name; + final String? abbr; + final String? help; + final String? valueHelp; + final Iterable? allowed; + final Map? allowedHelp; + final T? defaultsTo; + final void Function(T?)? callback; + final OptionType type; + final bool negatable; + final bool? splitCommas; + final bool mandatory; + final bool hide; + final List aliases; } diff --git a/lib/cli/option/base.dart b/lib/cli/option/base.dart index 70f8d21..c2cfe7d 100644 --- a/lib/cli/option/base.dart +++ b/lib/cli/option/base.dart @@ -3,8 +3,6 @@ import 'package:elixir/cli/extension.dart'; import 'package:elixir/cli/option.dart'; class BaseOption extends Option { - static const _name = 'base'; - BaseOption() : super( _name, @@ -13,6 +11,8 @@ class BaseOption extends Option { mandatory: true, ); + static const _name = 'base'; + static String base( ArgResults argResults, Never Function(String message) usageException, diff --git a/lib/cli/option/dir.dart b/lib/cli/option/dir.dart index c2f733a..d46de46 100644 --- a/lib/cli/option/dir.dart +++ b/lib/cli/option/dir.dart @@ -5,8 +5,6 @@ import 'package:elixir/cli/extension.dart'; import 'package:elixir/cli/option.dart'; class DirOption extends Option { - static const _name = 'dir'; - DirOption() : super( _name, @@ -15,6 +13,8 @@ class DirOption extends Option { mandatory: true, ); + static const _name = 'dir'; + static Directory dir( ArgResults argResults, Never Function(String message) usageException, diff --git a/lib/cli/option/head.dart b/lib/cli/option/head.dart index 51e4a26..30b4d4b 100644 --- a/lib/cli/option/head.dart +++ b/lib/cli/option/head.dart @@ -3,8 +3,6 @@ import 'package:elixir/cli/extension.dart'; import 'package:elixir/cli/option.dart'; class HeadOption extends Option { - static const _name = 'head'; - HeadOption() : super( _name, @@ -13,6 +11,8 @@ class HeadOption extends Option { mandatory: true, ); + static const _name = 'head'; + static String head( ArgResults argResults, Never Function(String message) usageException, diff --git a/lib/cli/option/num.dart b/lib/cli/option/num.dart index d2468a7..53adcb0 100644 --- a/lib/cli/option/num.dart +++ b/lib/cli/option/num.dart @@ -3,8 +3,6 @@ import 'package:elixir/cli/extension.dart'; import 'package:elixir/cli/option.dart'; class NumOption extends Option { - static const _name = 'num'; - NumOption() : super( _name, @@ -13,6 +11,8 @@ class NumOption extends Option { mandatory: true, ); + static const _name = 'num'; + static int num( ArgResults argResults, Never Function(String message) usageException, diff --git a/lib/cli/option/repo.dart b/lib/cli/option/repo.dart index 979c317..7884a03 100644 --- a/lib/cli/option/repo.dart +++ b/lib/cli/option/repo.dart @@ -3,8 +3,6 @@ import 'package:elixir/cli/extension.dart'; import 'package:elixir/cli/option.dart'; class RepoOption extends Option { - static const _name = 'repo'; - RepoOption() : super( _name, @@ -13,12 +11,14 @@ class RepoOption extends Option { mandatory: true, ); + static const _name = 'repo'; + static String repo( ArgResults argResults, Never Function(String message) usageException, ) { final repo = argResults.requireParam(_name); - if (!RegExp(r'^.+/.+').hasMatch(repo)) { + if (!RegExp('^.+/.+').hasMatch(repo)) { usageException('repo is not GitHub Repository: $repo'); } return repo; diff --git a/lib/cli/runner.dart b/lib/cli/runner.dart index c4601a3..8c55a12 100644 --- a/lib/cli/runner.dart +++ b/lib/cli/runner.dart @@ -1,11 +1,18 @@ +import 'dart:io'; + import 'package:args/command_runner.dart'; import 'package:elixir/cli/command/run.dart'; import 'package:elixir/cli/extension.dart'; import 'package:elixir/cli/flag/version.dart'; -import 'package:elixir/gen/pubspec.dart'; +import 'package:elixir/data/repository/github.dart'; +import 'package:elixir/gen/version.gen.dart'; +import 'package:elixir/infra/client.dart'; +import 'package:elixir/usecase/comment_indicates.dart'; +import 'package:elixir/usecase/delete_previous_comments.dart'; import 'package:elixir/util/log.dart'; +import 'package:http/http.dart'; -class ElixirCommandRunner extends CommandRunner { +class ElixirCommandRunner extends CommandRunner { ElixirCommandRunner() : super( 'Elixir', @@ -14,15 +21,35 @@ class ElixirCommandRunner extends CommandRunner { argParser.addFlags([ VersionFlag(), ]); - addCommand(RunCommand()); + + final token = Platform.environment['GITHUB_TOKEN'] ?? ''; + + final client = GitHubClient( + client: Client(), + token: token, + ); + + final repository = GitHubRepository(client: client); + final deletePreviousCommentsUseCase = DeletePreviousCommentsUseCase( + gitHubRepository: repository, + ); + final commentIndicatesUseCase = CommentIndicatesUseCase( + gitHubRepository: repository, + ); + addCommand( + RunCommand( + deletePreviousComments: deletePreviousCommentsUseCase, + commentIndicates: commentIndicatesUseCase, + ), + ); } @override - Future run(Iterable args) async { + Future run(Iterable args) async { final argResults = parse(args); if (VersionFlag.enabled(argResults)) { - log.i(pubspec.version); + log.i(packageVersion); return; } diff --git a/lib/data/keys.dart b/lib/data/keys.dart new file mode 100644 index 0000000..71401bd --- /dev/null +++ b/lib/data/keys.dart @@ -0,0 +1 @@ +const elixirKey = 'GENERATED_BY_ELIXIR'; diff --git a/lib/data/model/analysis.dart b/lib/data/model/analysis.dart deleted file mode 100644 index a7c89b2..0000000 --- a/lib/data/model/analysis.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'analysis.freezed.dart'; - -part 'analysis.g.dart'; - -@freezed -abstract class AnalysisResults with _$AnalysisResults { - const factory AnalysisResults({ - required int version, - required List diagnostics, - }) = _AnalysisResults; - - factory AnalysisResults.fromJson(Map json) => - _$AnalysisResultsFromJson(json); -} - -@freezed -abstract class Diagnostic with _$Diagnostic { - const factory Diagnostic({ - required String code, - required DiagnosticSeverity severity, - required DiagnosticType type, - required Location location, - required String problemMessage, - String? correctionMessage, - List? contextMessages, - Url? documentation, - }) = _Diagnostic; - - factory Diagnostic.fromJson(Map json) => - _$DiagnosticFromJson(json); -} - -enum DiagnosticSeverity { - @JsonValue('NONE') - none('NONE'), - @JsonValue('INFO') - info('INFO'), - @JsonValue('WARNING') - warning('WARNING'), - @JsonValue('ERROR') - error('ERROR'), - ; - - final String key; - - const DiagnosticSeverity(this.key); - - @override - String toString() => key; -} - -enum DiagnosticType { - @JsonValue('TODO') - todo('TODO'), - @JsonValue('HINT') - hint('HINT'), - @JsonValue('COMPILE_TIME_ERROR') - compileTimeError('COMPILE_TIME_ERROR'), - @JsonValue('CHECKED_MODE_COMPILE_TIME_ERROR') - checkedModeCompileTimeError('CHECKED_MODE_COMPILE_TIME_ERROR'), - @JsonValue('STATIC_WARNING') - staticWarning('STATIC_WARNING'), - @JsonValue('STATIC_TYPE_WARNING') - staticTypeWarning('STATIC_TYPE_WARNING'), - @JsonValue('SYNTACTIC_ERROR') - syntacticError('SYNTACTIC_ERROR'), - @JsonValue('LINT') - lint('LINT'), - ; - - final String key; - - const DiagnosticType(this.key); - - @override - String toString() => key; -} - -typedef FilePath = String; -typedef Url = String; - -@freezed -abstract class Location with _$Location { - const factory Location({ - required FilePath file, - required Range range, - }) = _Location; - - factory Location.fromJson(Map json) => - _$LocationFromJson(json); -} - -@freezed -abstract class Range with _$Range { - const factory Range({ - required Position start, - required Position end, - }) = _Range; - - factory Range.fromJson(Map json) => _$RangeFromJson(json); -} - -@freezed -abstract class Position with _$Position { - const factory Position({ - required int offset, - required int line, - required int column, - }) = _Position; - - factory Position.fromJson(Map json) => - _$PositionFromJson(json); -} - -@freezed -abstract class ContextMessage with _$ContextMessage { - const factory ContextMessage({ - required Location location, - required String message, - }) = _ContextMessage; - - factory ContextMessage.fromJson(Map json) => - _$ContextMessageFromJson(json); -} diff --git a/lib/data/model/analysis.freezed.dart b/lib/data/model/analysis.freezed.dart deleted file mode 100644 index 50b1c36..0000000 --- a/lib/data/model/analysis.freezed.dart +++ /dev/null @@ -1,1135 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target - -part of 'analysis.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -AnalysisResults _$AnalysisResultsFromJson(Map json) { - return _AnalysisResults.fromJson(json); -} - -/// @nodoc -mixin _$AnalysisResults { - int get version => throw _privateConstructorUsedError; - List get diagnostics => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $AnalysisResultsCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $AnalysisResultsCopyWith<$Res> { - factory $AnalysisResultsCopyWith( - AnalysisResults value, $Res Function(AnalysisResults) then) = - _$AnalysisResultsCopyWithImpl<$Res>; - $Res call({int version, List diagnostics}); -} - -/// @nodoc -class _$AnalysisResultsCopyWithImpl<$Res> - implements $AnalysisResultsCopyWith<$Res> { - _$AnalysisResultsCopyWithImpl(this._value, this._then); - - final AnalysisResults _value; - // ignore: unused_field - final $Res Function(AnalysisResults) _then; - - @override - $Res call({ - Object? version = freezed, - Object? diagnostics = freezed, - }) { - return _then(_value.copyWith( - version: version == freezed - ? _value.version - : version // ignore: cast_nullable_to_non_nullable - as int, - diagnostics: diagnostics == freezed - ? _value.diagnostics - : diagnostics // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -abstract class _$$_AnalysisResultsCopyWith<$Res> - implements $AnalysisResultsCopyWith<$Res> { - factory _$$_AnalysisResultsCopyWith( - _$_AnalysisResults value, $Res Function(_$_AnalysisResults) then) = - __$$_AnalysisResultsCopyWithImpl<$Res>; - @override - $Res call({int version, List diagnostics}); -} - -/// @nodoc -class __$$_AnalysisResultsCopyWithImpl<$Res> - extends _$AnalysisResultsCopyWithImpl<$Res> - implements _$$_AnalysisResultsCopyWith<$Res> { - __$$_AnalysisResultsCopyWithImpl( - _$_AnalysisResults _value, $Res Function(_$_AnalysisResults) _then) - : super(_value, (v) => _then(v as _$_AnalysisResults)); - - @override - _$_AnalysisResults get _value => super._value as _$_AnalysisResults; - - @override - $Res call({ - Object? version = freezed, - Object? diagnostics = freezed, - }) { - return _then(_$_AnalysisResults( - version: version == freezed - ? _value.version - : version // ignore: cast_nullable_to_non_nullable - as int, - diagnostics: diagnostics == freezed - ? _value._diagnostics - : diagnostics // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_AnalysisResults implements _AnalysisResults { - const _$_AnalysisResults( - {required this.version, required final List diagnostics}) - : _diagnostics = diagnostics; - - factory _$_AnalysisResults.fromJson(Map json) => - _$$_AnalysisResultsFromJson(json); - - @override - final int version; - final List _diagnostics; - @override - List get diagnostics { - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_diagnostics); - } - - @override - String toString() { - return 'AnalysisResults(version: $version, diagnostics: $diagnostics)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_AnalysisResults && - const DeepCollectionEquality().equals(other.version, version) && - const DeepCollectionEquality() - .equals(other._diagnostics, _diagnostics)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(version), - const DeepCollectionEquality().hash(_diagnostics)); - - @JsonKey(ignore: true) - @override - _$$_AnalysisResultsCopyWith<_$_AnalysisResults> get copyWith => - __$$_AnalysisResultsCopyWithImpl<_$_AnalysisResults>(this, _$identity); - - @override - Map toJson() { - return _$$_AnalysisResultsToJson(this); - } -} - -abstract class _AnalysisResults implements AnalysisResults { - const factory _AnalysisResults( - {required final int version, - required final List diagnostics}) = _$_AnalysisResults; - - factory _AnalysisResults.fromJson(Map json) = - _$_AnalysisResults.fromJson; - - @override - int get version => throw _privateConstructorUsedError; - @override - List get diagnostics => throw _privateConstructorUsedError; - @override - @JsonKey(ignore: true) - _$$_AnalysisResultsCopyWith<_$_AnalysisResults> get copyWith => - throw _privateConstructorUsedError; -} - -Diagnostic _$DiagnosticFromJson(Map json) { - return _Diagnostic.fromJson(json); -} - -/// @nodoc -mixin _$Diagnostic { - String get code => throw _privateConstructorUsedError; - DiagnosticSeverity get severity => throw _privateConstructorUsedError; - DiagnosticType get type => throw _privateConstructorUsedError; - Location get location => throw _privateConstructorUsedError; - String get problemMessage => throw _privateConstructorUsedError; - String? get correctionMessage => throw _privateConstructorUsedError; - List? get contextMessages => - throw _privateConstructorUsedError; - String? get documentation => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $DiagnosticCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $DiagnosticCopyWith<$Res> { - factory $DiagnosticCopyWith( - Diagnostic value, $Res Function(Diagnostic) then) = - _$DiagnosticCopyWithImpl<$Res>; - $Res call( - {String code, - DiagnosticSeverity severity, - DiagnosticType type, - Location location, - String problemMessage, - String? correctionMessage, - List? contextMessages, - String? documentation}); - - $LocationCopyWith<$Res> get location; -} - -/// @nodoc -class _$DiagnosticCopyWithImpl<$Res> implements $DiagnosticCopyWith<$Res> { - _$DiagnosticCopyWithImpl(this._value, this._then); - - final Diagnostic _value; - // ignore: unused_field - final $Res Function(Diagnostic) _then; - - @override - $Res call({ - Object? code = freezed, - Object? severity = freezed, - Object? type = freezed, - Object? location = freezed, - Object? problemMessage = freezed, - Object? correctionMessage = freezed, - Object? contextMessages = freezed, - Object? documentation = freezed, - }) { - return _then(_value.copyWith( - code: code == freezed - ? _value.code - : code // ignore: cast_nullable_to_non_nullable - as String, - severity: severity == freezed - ? _value.severity - : severity // ignore: cast_nullable_to_non_nullable - as DiagnosticSeverity, - type: type == freezed - ? _value.type - : type // ignore: cast_nullable_to_non_nullable - as DiagnosticType, - location: location == freezed - ? _value.location - : location // ignore: cast_nullable_to_non_nullable - as Location, - problemMessage: problemMessage == freezed - ? _value.problemMessage - : problemMessage // ignore: cast_nullable_to_non_nullable - as String, - correctionMessage: correctionMessage == freezed - ? _value.correctionMessage - : correctionMessage // ignore: cast_nullable_to_non_nullable - as String?, - contextMessages: contextMessages == freezed - ? _value.contextMessages - : contextMessages // ignore: cast_nullable_to_non_nullable - as List?, - documentation: documentation == freezed - ? _value.documentation - : documentation // ignore: cast_nullable_to_non_nullable - as String?, - )); - } - - @override - $LocationCopyWith<$Res> get location { - return $LocationCopyWith<$Res>(_value.location, (value) { - return _then(_value.copyWith(location: value)); - }); - } -} - -/// @nodoc -abstract class _$$_DiagnosticCopyWith<$Res> - implements $DiagnosticCopyWith<$Res> { - factory _$$_DiagnosticCopyWith( - _$_Diagnostic value, $Res Function(_$_Diagnostic) then) = - __$$_DiagnosticCopyWithImpl<$Res>; - @override - $Res call( - {String code, - DiagnosticSeverity severity, - DiagnosticType type, - Location location, - String problemMessage, - String? correctionMessage, - List? contextMessages, - String? documentation}); - - @override - $LocationCopyWith<$Res> get location; -} - -/// @nodoc -class __$$_DiagnosticCopyWithImpl<$Res> extends _$DiagnosticCopyWithImpl<$Res> - implements _$$_DiagnosticCopyWith<$Res> { - __$$_DiagnosticCopyWithImpl( - _$_Diagnostic _value, $Res Function(_$_Diagnostic) _then) - : super(_value, (v) => _then(v as _$_Diagnostic)); - - @override - _$_Diagnostic get _value => super._value as _$_Diagnostic; - - @override - $Res call({ - Object? code = freezed, - Object? severity = freezed, - Object? type = freezed, - Object? location = freezed, - Object? problemMessage = freezed, - Object? correctionMessage = freezed, - Object? contextMessages = freezed, - Object? documentation = freezed, - }) { - return _then(_$_Diagnostic( - code: code == freezed - ? _value.code - : code // ignore: cast_nullable_to_non_nullable - as String, - severity: severity == freezed - ? _value.severity - : severity // ignore: cast_nullable_to_non_nullable - as DiagnosticSeverity, - type: type == freezed - ? _value.type - : type // ignore: cast_nullable_to_non_nullable - as DiagnosticType, - location: location == freezed - ? _value.location - : location // ignore: cast_nullable_to_non_nullable - as Location, - problemMessage: problemMessage == freezed - ? _value.problemMessage - : problemMessage // ignore: cast_nullable_to_non_nullable - as String, - correctionMessage: correctionMessage == freezed - ? _value.correctionMessage - : correctionMessage // ignore: cast_nullable_to_non_nullable - as String?, - contextMessages: contextMessages == freezed - ? _value._contextMessages - : contextMessages // ignore: cast_nullable_to_non_nullable - as List?, - documentation: documentation == freezed - ? _value.documentation - : documentation // ignore: cast_nullable_to_non_nullable - as String?, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_Diagnostic implements _Diagnostic { - const _$_Diagnostic( - {required this.code, - required this.severity, - required this.type, - required this.location, - required this.problemMessage, - this.correctionMessage, - final List? contextMessages, - this.documentation}) - : _contextMessages = contextMessages; - - factory _$_Diagnostic.fromJson(Map json) => - _$$_DiagnosticFromJson(json); - - @override - final String code; - @override - final DiagnosticSeverity severity; - @override - final DiagnosticType type; - @override - final Location location; - @override - final String problemMessage; - @override - final String? correctionMessage; - final List? _contextMessages; - @override - List? get contextMessages { - final value = _contextMessages; - if (value == null) return null; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(value); - } - - @override - final String? documentation; - - @override - String toString() { - return 'Diagnostic(code: $code, severity: $severity, type: $type, location: $location, problemMessage: $problemMessage, correctionMessage: $correctionMessage, contextMessages: $contextMessages, documentation: $documentation)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_Diagnostic && - const DeepCollectionEquality().equals(other.code, code) && - const DeepCollectionEquality().equals(other.severity, severity) && - const DeepCollectionEquality().equals(other.type, type) && - const DeepCollectionEquality().equals(other.location, location) && - const DeepCollectionEquality() - .equals(other.problemMessage, problemMessage) && - const DeepCollectionEquality() - .equals(other.correctionMessage, correctionMessage) && - const DeepCollectionEquality() - .equals(other._contextMessages, _contextMessages) && - const DeepCollectionEquality() - .equals(other.documentation, documentation)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(code), - const DeepCollectionEquality().hash(severity), - const DeepCollectionEquality().hash(type), - const DeepCollectionEquality().hash(location), - const DeepCollectionEquality().hash(problemMessage), - const DeepCollectionEquality().hash(correctionMessage), - const DeepCollectionEquality().hash(_contextMessages), - const DeepCollectionEquality().hash(documentation)); - - @JsonKey(ignore: true) - @override - _$$_DiagnosticCopyWith<_$_Diagnostic> get copyWith => - __$$_DiagnosticCopyWithImpl<_$_Diagnostic>(this, _$identity); - - @override - Map toJson() { - return _$$_DiagnosticToJson(this); - } -} - -abstract class _Diagnostic implements Diagnostic { - const factory _Diagnostic( - {required final String code, - required final DiagnosticSeverity severity, - required final DiagnosticType type, - required final Location location, - required final String problemMessage, - final String? correctionMessage, - final List? contextMessages, - final String? documentation}) = _$_Diagnostic; - - factory _Diagnostic.fromJson(Map json) = - _$_Diagnostic.fromJson; - - @override - String get code => throw _privateConstructorUsedError; - @override - DiagnosticSeverity get severity => throw _privateConstructorUsedError; - @override - DiagnosticType get type => throw _privateConstructorUsedError; - @override - Location get location => throw _privateConstructorUsedError; - @override - String get problemMessage => throw _privateConstructorUsedError; - @override - String? get correctionMessage => throw _privateConstructorUsedError; - @override - List? get contextMessages => - throw _privateConstructorUsedError; - @override - String? get documentation => throw _privateConstructorUsedError; - @override - @JsonKey(ignore: true) - _$$_DiagnosticCopyWith<_$_Diagnostic> get copyWith => - throw _privateConstructorUsedError; -} - -Location _$LocationFromJson(Map json) { - return _Location.fromJson(json); -} - -/// @nodoc -mixin _$Location { - String get file => throw _privateConstructorUsedError; - Range get range => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $LocationCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $LocationCopyWith<$Res> { - factory $LocationCopyWith(Location value, $Res Function(Location) then) = - _$LocationCopyWithImpl<$Res>; - $Res call({String file, Range range}); - - $RangeCopyWith<$Res> get range; -} - -/// @nodoc -class _$LocationCopyWithImpl<$Res> implements $LocationCopyWith<$Res> { - _$LocationCopyWithImpl(this._value, this._then); - - final Location _value; - // ignore: unused_field - final $Res Function(Location) _then; - - @override - $Res call({ - Object? file = freezed, - Object? range = freezed, - }) { - return _then(_value.copyWith( - file: file == freezed - ? _value.file - : file // ignore: cast_nullable_to_non_nullable - as String, - range: range == freezed - ? _value.range - : range // ignore: cast_nullable_to_non_nullable - as Range, - )); - } - - @override - $RangeCopyWith<$Res> get range { - return $RangeCopyWith<$Res>(_value.range, (value) { - return _then(_value.copyWith(range: value)); - }); - } -} - -/// @nodoc -abstract class _$$_LocationCopyWith<$Res> implements $LocationCopyWith<$Res> { - factory _$$_LocationCopyWith( - _$_Location value, $Res Function(_$_Location) then) = - __$$_LocationCopyWithImpl<$Res>; - @override - $Res call({String file, Range range}); - - @override - $RangeCopyWith<$Res> get range; -} - -/// @nodoc -class __$$_LocationCopyWithImpl<$Res> extends _$LocationCopyWithImpl<$Res> - implements _$$_LocationCopyWith<$Res> { - __$$_LocationCopyWithImpl( - _$_Location _value, $Res Function(_$_Location) _then) - : super(_value, (v) => _then(v as _$_Location)); - - @override - _$_Location get _value => super._value as _$_Location; - - @override - $Res call({ - Object? file = freezed, - Object? range = freezed, - }) { - return _then(_$_Location( - file: file == freezed - ? _value.file - : file // ignore: cast_nullable_to_non_nullable - as String, - range: range == freezed - ? _value.range - : range // ignore: cast_nullable_to_non_nullable - as Range, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_Location implements _Location { - const _$_Location({required this.file, required this.range}); - - factory _$_Location.fromJson(Map json) => - _$$_LocationFromJson(json); - - @override - final String file; - @override - final Range range; - - @override - String toString() { - return 'Location(file: $file, range: $range)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_Location && - const DeepCollectionEquality().equals(other.file, file) && - const DeepCollectionEquality().equals(other.range, range)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(file), - const DeepCollectionEquality().hash(range)); - - @JsonKey(ignore: true) - @override - _$$_LocationCopyWith<_$_Location> get copyWith => - __$$_LocationCopyWithImpl<_$_Location>(this, _$identity); - - @override - Map toJson() { - return _$$_LocationToJson(this); - } -} - -abstract class _Location implements Location { - const factory _Location( - {required final String file, required final Range range}) = _$_Location; - - factory _Location.fromJson(Map json) = _$_Location.fromJson; - - @override - String get file => throw _privateConstructorUsedError; - @override - Range get range => throw _privateConstructorUsedError; - @override - @JsonKey(ignore: true) - _$$_LocationCopyWith<_$_Location> get copyWith => - throw _privateConstructorUsedError; -} - -Range _$RangeFromJson(Map json) { - return _Range.fromJson(json); -} - -/// @nodoc -mixin _$Range { - Position get start => throw _privateConstructorUsedError; - Position get end => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $RangeCopyWith get copyWith => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $RangeCopyWith<$Res> { - factory $RangeCopyWith(Range value, $Res Function(Range) then) = - _$RangeCopyWithImpl<$Res>; - $Res call({Position start, Position end}); - - $PositionCopyWith<$Res> get start; - $PositionCopyWith<$Res> get end; -} - -/// @nodoc -class _$RangeCopyWithImpl<$Res> implements $RangeCopyWith<$Res> { - _$RangeCopyWithImpl(this._value, this._then); - - final Range _value; - // ignore: unused_field - final $Res Function(Range) _then; - - @override - $Res call({ - Object? start = freezed, - Object? end = freezed, - }) { - return _then(_value.copyWith( - start: start == freezed - ? _value.start - : start // ignore: cast_nullable_to_non_nullable - as Position, - end: end == freezed - ? _value.end - : end // ignore: cast_nullable_to_non_nullable - as Position, - )); - } - - @override - $PositionCopyWith<$Res> get start { - return $PositionCopyWith<$Res>(_value.start, (value) { - return _then(_value.copyWith(start: value)); - }); - } - - @override - $PositionCopyWith<$Res> get end { - return $PositionCopyWith<$Res>(_value.end, (value) { - return _then(_value.copyWith(end: value)); - }); - } -} - -/// @nodoc -abstract class _$$_RangeCopyWith<$Res> implements $RangeCopyWith<$Res> { - factory _$$_RangeCopyWith(_$_Range value, $Res Function(_$_Range) then) = - __$$_RangeCopyWithImpl<$Res>; - @override - $Res call({Position start, Position end}); - - @override - $PositionCopyWith<$Res> get start; - @override - $PositionCopyWith<$Res> get end; -} - -/// @nodoc -class __$$_RangeCopyWithImpl<$Res> extends _$RangeCopyWithImpl<$Res> - implements _$$_RangeCopyWith<$Res> { - __$$_RangeCopyWithImpl(_$_Range _value, $Res Function(_$_Range) _then) - : super(_value, (v) => _then(v as _$_Range)); - - @override - _$_Range get _value => super._value as _$_Range; - - @override - $Res call({ - Object? start = freezed, - Object? end = freezed, - }) { - return _then(_$_Range( - start: start == freezed - ? _value.start - : start // ignore: cast_nullable_to_non_nullable - as Position, - end: end == freezed - ? _value.end - : end // ignore: cast_nullable_to_non_nullable - as Position, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_Range implements _Range { - const _$_Range({required this.start, required this.end}); - - factory _$_Range.fromJson(Map json) => - _$$_RangeFromJson(json); - - @override - final Position start; - @override - final Position end; - - @override - String toString() { - return 'Range(start: $start, end: $end)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_Range && - const DeepCollectionEquality().equals(other.start, start) && - const DeepCollectionEquality().equals(other.end, end)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(start), - const DeepCollectionEquality().hash(end)); - - @JsonKey(ignore: true) - @override - _$$_RangeCopyWith<_$_Range> get copyWith => - __$$_RangeCopyWithImpl<_$_Range>(this, _$identity); - - @override - Map toJson() { - return _$$_RangeToJson(this); - } -} - -abstract class _Range implements Range { - const factory _Range( - {required final Position start, required final Position end}) = _$_Range; - - factory _Range.fromJson(Map json) = _$_Range.fromJson; - - @override - Position get start => throw _privateConstructorUsedError; - @override - Position get end => throw _privateConstructorUsedError; - @override - @JsonKey(ignore: true) - _$$_RangeCopyWith<_$_Range> get copyWith => - throw _privateConstructorUsedError; -} - -Position _$PositionFromJson(Map json) { - return _Position.fromJson(json); -} - -/// @nodoc -mixin _$Position { - int get offset => throw _privateConstructorUsedError; - int get line => throw _privateConstructorUsedError; - int get column => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $PositionCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $PositionCopyWith<$Res> { - factory $PositionCopyWith(Position value, $Res Function(Position) then) = - _$PositionCopyWithImpl<$Res>; - $Res call({int offset, int line, int column}); -} - -/// @nodoc -class _$PositionCopyWithImpl<$Res> implements $PositionCopyWith<$Res> { - _$PositionCopyWithImpl(this._value, this._then); - - final Position _value; - // ignore: unused_field - final $Res Function(Position) _then; - - @override - $Res call({ - Object? offset = freezed, - Object? line = freezed, - Object? column = freezed, - }) { - return _then(_value.copyWith( - offset: offset == freezed - ? _value.offset - : offset // ignore: cast_nullable_to_non_nullable - as int, - line: line == freezed - ? _value.line - : line // ignore: cast_nullable_to_non_nullable - as int, - column: column == freezed - ? _value.column - : column // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -abstract class _$$_PositionCopyWith<$Res> implements $PositionCopyWith<$Res> { - factory _$$_PositionCopyWith( - _$_Position value, $Res Function(_$_Position) then) = - __$$_PositionCopyWithImpl<$Res>; - @override - $Res call({int offset, int line, int column}); -} - -/// @nodoc -class __$$_PositionCopyWithImpl<$Res> extends _$PositionCopyWithImpl<$Res> - implements _$$_PositionCopyWith<$Res> { - __$$_PositionCopyWithImpl( - _$_Position _value, $Res Function(_$_Position) _then) - : super(_value, (v) => _then(v as _$_Position)); - - @override - _$_Position get _value => super._value as _$_Position; - - @override - $Res call({ - Object? offset = freezed, - Object? line = freezed, - Object? column = freezed, - }) { - return _then(_$_Position( - offset: offset == freezed - ? _value.offset - : offset // ignore: cast_nullable_to_non_nullable - as int, - line: line == freezed - ? _value.line - : line // ignore: cast_nullable_to_non_nullable - as int, - column: column == freezed - ? _value.column - : column // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_Position implements _Position { - const _$_Position( - {required this.offset, required this.line, required this.column}); - - factory _$_Position.fromJson(Map json) => - _$$_PositionFromJson(json); - - @override - final int offset; - @override - final int line; - @override - final int column; - - @override - String toString() { - return 'Position(offset: $offset, line: $line, column: $column)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_Position && - const DeepCollectionEquality().equals(other.offset, offset) && - const DeepCollectionEquality().equals(other.line, line) && - const DeepCollectionEquality().equals(other.column, column)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(offset), - const DeepCollectionEquality().hash(line), - const DeepCollectionEquality().hash(column)); - - @JsonKey(ignore: true) - @override - _$$_PositionCopyWith<_$_Position> get copyWith => - __$$_PositionCopyWithImpl<_$_Position>(this, _$identity); - - @override - Map toJson() { - return _$$_PositionToJson(this); - } -} - -abstract class _Position implements Position { - const factory _Position( - {required final int offset, - required final int line, - required final int column}) = _$_Position; - - factory _Position.fromJson(Map json) = _$_Position.fromJson; - - @override - int get offset => throw _privateConstructorUsedError; - @override - int get line => throw _privateConstructorUsedError; - @override - int get column => throw _privateConstructorUsedError; - @override - @JsonKey(ignore: true) - _$$_PositionCopyWith<_$_Position> get copyWith => - throw _privateConstructorUsedError; -} - -ContextMessage _$ContextMessageFromJson(Map json) { - return _ContextMessage.fromJson(json); -} - -/// @nodoc -mixin _$ContextMessage { - Location get location => throw _privateConstructorUsedError; - String get message => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $ContextMessageCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ContextMessageCopyWith<$Res> { - factory $ContextMessageCopyWith( - ContextMessage value, $Res Function(ContextMessage) then) = - _$ContextMessageCopyWithImpl<$Res>; - $Res call({Location location, String message}); - - $LocationCopyWith<$Res> get location; -} - -/// @nodoc -class _$ContextMessageCopyWithImpl<$Res> - implements $ContextMessageCopyWith<$Res> { - _$ContextMessageCopyWithImpl(this._value, this._then); - - final ContextMessage _value; - // ignore: unused_field - final $Res Function(ContextMessage) _then; - - @override - $Res call({ - Object? location = freezed, - Object? message = freezed, - }) { - return _then(_value.copyWith( - location: location == freezed - ? _value.location - : location // ignore: cast_nullable_to_non_nullable - as Location, - message: message == freezed - ? _value.message - : message // ignore: cast_nullable_to_non_nullable - as String, - )); - } - - @override - $LocationCopyWith<$Res> get location { - return $LocationCopyWith<$Res>(_value.location, (value) { - return _then(_value.copyWith(location: value)); - }); - } -} - -/// @nodoc -abstract class _$$_ContextMessageCopyWith<$Res> - implements $ContextMessageCopyWith<$Res> { - factory _$$_ContextMessageCopyWith( - _$_ContextMessage value, $Res Function(_$_ContextMessage) then) = - __$$_ContextMessageCopyWithImpl<$Res>; - @override - $Res call({Location location, String message}); - - @override - $LocationCopyWith<$Res> get location; -} - -/// @nodoc -class __$$_ContextMessageCopyWithImpl<$Res> - extends _$ContextMessageCopyWithImpl<$Res> - implements _$$_ContextMessageCopyWith<$Res> { - __$$_ContextMessageCopyWithImpl( - _$_ContextMessage _value, $Res Function(_$_ContextMessage) _then) - : super(_value, (v) => _then(v as _$_ContextMessage)); - - @override - _$_ContextMessage get _value => super._value as _$_ContextMessage; - - @override - $Res call({ - Object? location = freezed, - Object? message = freezed, - }) { - return _then(_$_ContextMessage( - location: location == freezed - ? _value.location - : location // ignore: cast_nullable_to_non_nullable - as Location, - message: message == freezed - ? _value.message - : message // ignore: cast_nullable_to_non_nullable - as String, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_ContextMessage implements _ContextMessage { - const _$_ContextMessage({required this.location, required this.message}); - - factory _$_ContextMessage.fromJson(Map json) => - _$$_ContextMessageFromJson(json); - - @override - final Location location; - @override - final String message; - - @override - String toString() { - return 'ContextMessage(location: $location, message: $message)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_ContextMessage && - const DeepCollectionEquality().equals(other.location, location) && - const DeepCollectionEquality().equals(other.message, message)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(location), - const DeepCollectionEquality().hash(message)); - - @JsonKey(ignore: true) - @override - _$$_ContextMessageCopyWith<_$_ContextMessage> get copyWith => - __$$_ContextMessageCopyWithImpl<_$_ContextMessage>(this, _$identity); - - @override - Map toJson() { - return _$$_ContextMessageToJson(this); - } -} - -abstract class _ContextMessage implements ContextMessage { - const factory _ContextMessage( - {required final Location location, - required final String message}) = _$_ContextMessage; - - factory _ContextMessage.fromJson(Map json) = - _$_ContextMessage.fromJson; - - @override - Location get location => throw _privateConstructorUsedError; - @override - String get message => throw _privateConstructorUsedError; - @override - @JsonKey(ignore: true) - _$$_ContextMessageCopyWith<_$_ContextMessage> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/data/model/analysis.g.dart b/lib/data/model/analysis.g.dart deleted file mode 100644 index f10895b..0000000 --- a/lib/data/model/analysis.g.dart +++ /dev/null @@ -1,113 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -// ignore_for_file: non_constant_identifier_names - -part of 'analysis.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$_AnalysisResults _$$_AnalysisResultsFromJson(Map json) => - _$_AnalysisResults( - version: json['version'] as int, - diagnostics: (json['diagnostics'] as List) - .map((e) => Diagnostic.fromJson(e as Map)) - .toList(), - ); - -Map _$$_AnalysisResultsToJson(_$_AnalysisResults instance) => - { - 'version': instance.version, - 'diagnostics': instance.diagnostics, - }; - -_$_Diagnostic _$$_DiagnosticFromJson(Map json) => - _$_Diagnostic( - code: json['code'] as String, - severity: $enumDecode(_$DiagnosticSeverityEnumMap, json['severity']), - type: $enumDecode(_$DiagnosticTypeEnumMap, json['type']), - location: Location.fromJson(json['location'] as Map), - problemMessage: json['problemMessage'] as String, - correctionMessage: json['correctionMessage'] as String?, - contextMessages: (json['contextMessages'] as List?) - ?.map((e) => ContextMessage.fromJson(e as Map)) - .toList(), - documentation: json['documentation'] as String?, - ); - -Map _$$_DiagnosticToJson(_$_Diagnostic instance) => - { - 'code': instance.code, - 'severity': _$DiagnosticSeverityEnumMap[instance.severity], - 'type': _$DiagnosticTypeEnumMap[instance.type], - 'location': instance.location, - 'problemMessage': instance.problemMessage, - 'correctionMessage': instance.correctionMessage, - 'contextMessages': instance.contextMessages, - 'documentation': instance.documentation, - }; - -const _$DiagnosticSeverityEnumMap = { - DiagnosticSeverity.none: 'NONE', - DiagnosticSeverity.info: 'INFO', - DiagnosticSeverity.warning: 'WARNING', - DiagnosticSeverity.error: 'ERROR', -}; - -const _$DiagnosticTypeEnumMap = { - DiagnosticType.todo: 'TODO', - DiagnosticType.hint: 'HINT', - DiagnosticType.compileTimeError: 'COMPILE_TIME_ERROR', - DiagnosticType.checkedModeCompileTimeError: 'CHECKED_MODE_COMPILE_TIME_ERROR', - DiagnosticType.staticWarning: 'STATIC_WARNING', - DiagnosticType.staticTypeWarning: 'STATIC_TYPE_WARNING', - DiagnosticType.syntacticError: 'SYNTACTIC_ERROR', - DiagnosticType.lint: 'LINT', -}; - -_$_Location _$$_LocationFromJson(Map json) => _$_Location( - file: json['file'] as String, - range: Range.fromJson(json['range'] as Map), - ); - -Map _$$_LocationToJson(_$_Location instance) => - { - 'file': instance.file, - 'range': instance.range, - }; - -_$_Range _$$_RangeFromJson(Map json) => _$_Range( - start: Position.fromJson(json['start'] as Map), - end: Position.fromJson(json['end'] as Map), - ); - -Map _$$_RangeToJson(_$_Range instance) => { - 'start': instance.start, - 'end': instance.end, - }; - -_$_Position _$$_PositionFromJson(Map json) => _$_Position( - offset: json['offset'] as int, - line: json['line'] as int, - column: json['column'] as int, - ); - -Map _$$_PositionToJson(_$_Position instance) => - { - 'offset': instance.offset, - 'line': instance.line, - 'column': instance.column, - }; - -_$_ContextMessage _$$_ContextMessageFromJson(Map json) => - _$_ContextMessage( - location: Location.fromJson(json['location'] as Map), - message: json['message'] as String, - ); - -Map _$$_ContextMessageToJson(_$_ContextMessage instance) => - { - 'location': instance.location, - 'message': instance.message, - }; diff --git a/lib/data/model/analyze_result.dart b/lib/data/model/analyze_result.dart new file mode 100644 index 0000000..be73643 --- /dev/null +++ b/lib/data/model/analyze_result.dart @@ -0,0 +1,55 @@ +import 'package:meta/meta.dart'; + +@immutable +class AnalyzeResult { + const AnalyzeResult({ + required this.severity, + required this.type, + required this.errorCode, + required this.filePath, + required this.line, + required this.column, + required this.length, + required this.errorMessage, + }); + + final Severity severity; + final String type; + final String errorCode; + final String filePath; + final int line; + final int column; + final int length; + final String errorMessage; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is AnalyzeResult && + runtimeType == other.runtimeType && + severity == other.severity && + type == other.type && + errorCode == other.errorCode && + filePath == other.filePath && + line == other.line && + column == other.column && + length == other.length && + errorMessage == other.errorMessage; + + @override + int get hashCode => + severity.hashCode ^ + type.hashCode ^ + errorCode.hashCode ^ + filePath.hashCode ^ + line.hashCode ^ + column.hashCode ^ + length.hashCode ^ + errorMessage.hashCode; +} + +enum Severity { + info, + warning, + error, +} diff --git a/lib/data/model/comment.dart b/lib/data/model/comment.dart new file mode 100644 index 0000000..62fca4b --- /dev/null +++ b/lib/data/model/comment.dart @@ -0,0 +1,17 @@ +class Comment { + const Comment({ + required this.path, + required this.line, + required this.body, + }); + + final String path; + final int line; + final String body; + + Map toJson() => { + 'path': path, + 'line': line, + 'body': body, + }; +} diff --git a/lib/data/model/exception/github.dart b/lib/data/model/exception/github.dart new file mode 100644 index 0000000..ac36f66 --- /dev/null +++ b/lib/data/model/exception/github.dart @@ -0,0 +1,63 @@ +import 'package:meta/meta.dart'; + +@immutable +abstract class GitHubException implements Exception { + const GitHubException(this.message); + + final String message; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is GitHubException && + runtimeType == other.runtimeType && + message == other.message; + + @override + int get hashCode => message.hashCode; +} + +/// 400 +class BadRequest extends GitHubException { + const BadRequest() : super('Bad Request'); +} + +/// 401 +class RequiresAuthentication extends GitHubException { + const RequiresAuthentication() : super('Requires authentication'); +} + +/// 403 +class Forbidden extends GitHubException { + const Forbidden() : super('Forbidden'); +} + +/// 404 +class NotFound extends GitHubException { + const NotFound() : super('Resource not found'); +} + +/// 422 +class ValidationFailed extends GitHubException { + const ValidationFailed() : super('Validation Failed'); +} + +/// 429 +class TooManyRequests extends GitHubException { + const TooManyRequests() : super('Too Many Requests'); +} + +/// 500 +class InternalError extends GitHubException { + const InternalError() : super('Internal Error'); +} + +/// 503 +class ServiceUnavailable extends GitHubException { + const ServiceUnavailable() : super('Service unavailable'); +} + +/// Other +class Unknown extends GitHubException { + const Unknown() : super('Unknown'); +} diff --git a/lib/data/model/indicate.dart b/lib/data/model/indicate.dart index b373bbb..5a82256 100644 --- a/lib/data/model/indicate.dart +++ b/lib/data/model/indicate.dart @@ -1,19 +1,11 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'indicate.freezed.dart'; - -part 'indicate.g.dart'; - -@freezed -abstract class Indicate with _$Indicate { - const factory Indicate({ - required String diagHash, - required String body, - required String commitId, - required String path, - required int line, - }) = _Indicate; - - factory Indicate.fromJson(Map json) => - _$IndicateFromJson(json); +class Indicate { + const Indicate({ + required this.path, + required this.line, + required this.message, + }); + + final String path; + final int line; + final String message; } diff --git a/lib/data/model/indicate.freezed.dart b/lib/data/model/indicate.freezed.dart deleted file mode 100644 index f592326..0000000 --- a/lib/data/model/indicate.freezed.dart +++ /dev/null @@ -1,223 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target - -part of 'indicate.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -Indicate _$IndicateFromJson(Map json) { - return _Indicate.fromJson(json); -} - -/// @nodoc -mixin _$Indicate { - String get diagHash => throw _privateConstructorUsedError; - String get body => throw _privateConstructorUsedError; - String get commitId => throw _privateConstructorUsedError; - String get path => throw _privateConstructorUsedError; - int get line => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $IndicateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $IndicateCopyWith<$Res> { - factory $IndicateCopyWith(Indicate value, $Res Function(Indicate) then) = - _$IndicateCopyWithImpl<$Res>; - $Res call( - {String diagHash, String body, String commitId, String path, int line}); -} - -/// @nodoc -class _$IndicateCopyWithImpl<$Res> implements $IndicateCopyWith<$Res> { - _$IndicateCopyWithImpl(this._value, this._then); - - final Indicate _value; - // ignore: unused_field - final $Res Function(Indicate) _then; - - @override - $Res call({ - Object? diagHash = freezed, - Object? body = freezed, - Object? commitId = freezed, - Object? path = freezed, - Object? line = freezed, - }) { - return _then(_value.copyWith( - diagHash: diagHash == freezed - ? _value.diagHash - : diagHash // ignore: cast_nullable_to_non_nullable - as String, - body: body == freezed - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - commitId: commitId == freezed - ? _value.commitId - : commitId // ignore: cast_nullable_to_non_nullable - as String, - path: path == freezed - ? _value.path - : path // ignore: cast_nullable_to_non_nullable - as String, - line: line == freezed - ? _value.line - : line // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -abstract class _$$_IndicateCopyWith<$Res> implements $IndicateCopyWith<$Res> { - factory _$$_IndicateCopyWith( - _$_Indicate value, $Res Function(_$_Indicate) then) = - __$$_IndicateCopyWithImpl<$Res>; - @override - $Res call( - {String diagHash, String body, String commitId, String path, int line}); -} - -/// @nodoc -class __$$_IndicateCopyWithImpl<$Res> extends _$IndicateCopyWithImpl<$Res> - implements _$$_IndicateCopyWith<$Res> { - __$$_IndicateCopyWithImpl( - _$_Indicate _value, $Res Function(_$_Indicate) _then) - : super(_value, (v) => _then(v as _$_Indicate)); - - @override - _$_Indicate get _value => super._value as _$_Indicate; - - @override - $Res call({ - Object? diagHash = freezed, - Object? body = freezed, - Object? commitId = freezed, - Object? path = freezed, - Object? line = freezed, - }) { - return _then(_$_Indicate( - diagHash: diagHash == freezed - ? _value.diagHash - : diagHash // ignore: cast_nullable_to_non_nullable - as String, - body: body == freezed - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - commitId: commitId == freezed - ? _value.commitId - : commitId // ignore: cast_nullable_to_non_nullable - as String, - path: path == freezed - ? _value.path - : path // ignore: cast_nullable_to_non_nullable - as String, - line: line == freezed - ? _value.line - : line // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_Indicate implements _Indicate { - const _$_Indicate( - {required this.diagHash, - required this.body, - required this.commitId, - required this.path, - required this.line}); - - factory _$_Indicate.fromJson(Map json) => - _$$_IndicateFromJson(json); - - @override - final String diagHash; - @override - final String body; - @override - final String commitId; - @override - final String path; - @override - final int line; - - @override - String toString() { - return 'Indicate(diagHash: $diagHash, body: $body, commitId: $commitId, path: $path, line: $line)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_Indicate && - const DeepCollectionEquality().equals(other.diagHash, diagHash) && - const DeepCollectionEquality().equals(other.body, body) && - const DeepCollectionEquality().equals(other.commitId, commitId) && - const DeepCollectionEquality().equals(other.path, path) && - const DeepCollectionEquality().equals(other.line, line)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(diagHash), - const DeepCollectionEquality().hash(body), - const DeepCollectionEquality().hash(commitId), - const DeepCollectionEquality().hash(path), - const DeepCollectionEquality().hash(line)); - - @JsonKey(ignore: true) - @override - _$$_IndicateCopyWith<_$_Indicate> get copyWith => - __$$_IndicateCopyWithImpl<_$_Indicate>(this, _$identity); - - @override - Map toJson() { - return _$$_IndicateToJson(this); - } -} - -abstract class _Indicate implements Indicate { - const factory _Indicate( - {required final String diagHash, - required final String body, - required final String commitId, - required final String path, - required final int line}) = _$_Indicate; - - factory _Indicate.fromJson(Map json) = _$_Indicate.fromJson; - - @override - String get diagHash => throw _privateConstructorUsedError; - @override - String get body => throw _privateConstructorUsedError; - @override - String get commitId => throw _privateConstructorUsedError; - @override - String get path => throw _privateConstructorUsedError; - @override - int get line => throw _privateConstructorUsedError; - @override - @JsonKey(ignore: true) - _$$_IndicateCopyWith<_$_Indicate> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/data/model/indicate.g.dart b/lib/data/model/indicate.g.dart deleted file mode 100644 index 77402e6..0000000 --- a/lib/data/model/indicate.g.dart +++ /dev/null @@ -1,26 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -// ignore_for_file: non_constant_identifier_names - -part of 'indicate.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$_Indicate _$$_IndicateFromJson(Map json) => _$_Indicate( - diagHash: json['diagHash'] as String, - body: json['body'] as String, - commitId: json['commitId'] as String, - path: json['path'] as String, - line: json['line'] as int, - ); - -Map _$$_IndicateToJson(_$_Indicate instance) => - { - 'diagHash': instance.diagHash, - 'body': instance.body, - 'commitId': instance.commitId, - 'path': instance.path, - 'line': instance.line, - }; diff --git a/lib/data/model/review_comment.dart b/lib/data/model/review_comment.dart deleted file mode 100644 index 2f25c51..0000000 --- a/lib/data/model/review_comment.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'review_comment.freezed.dart'; - -part 'review_comment.g.dart'; - -@freezed -class ReviewComment with _$ReviewComment { - @JsonSerializable(fieldRename: FieldRename.snake) - const factory ReviewComment({ - required String path, - required String originalCommitId, - required String body, - int? line, - }) = _ReviewComment; - - factory ReviewComment.fromJson(Map json) => - _$ReviewCommentFromJson(json); -} diff --git a/lib/data/model/review_comment.freezed.dart b/lib/data/model/review_comment.freezed.dart deleted file mode 100644 index c59b855..0000000 --- a/lib/data/model/review_comment.freezed.dart +++ /dev/null @@ -1,209 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target - -part of 'review_comment.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -ReviewComment _$ReviewCommentFromJson(Map json) { - return _ReviewComment.fromJson(json); -} - -/// @nodoc -mixin _$ReviewComment { - String get path => throw _privateConstructorUsedError; - String get originalCommitId => throw _privateConstructorUsedError; - String get body => throw _privateConstructorUsedError; - int? get line => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $ReviewCommentCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ReviewCommentCopyWith<$Res> { - factory $ReviewCommentCopyWith( - ReviewComment value, $Res Function(ReviewComment) then) = - _$ReviewCommentCopyWithImpl<$Res>; - $Res call({String path, String originalCommitId, String body, int? line}); -} - -/// @nodoc -class _$ReviewCommentCopyWithImpl<$Res> - implements $ReviewCommentCopyWith<$Res> { - _$ReviewCommentCopyWithImpl(this._value, this._then); - - final ReviewComment _value; - // ignore: unused_field - final $Res Function(ReviewComment) _then; - - @override - $Res call({ - Object? path = freezed, - Object? originalCommitId = freezed, - Object? body = freezed, - Object? line = freezed, - }) { - return _then(_value.copyWith( - path: path == freezed - ? _value.path - : path // ignore: cast_nullable_to_non_nullable - as String, - originalCommitId: originalCommitId == freezed - ? _value.originalCommitId - : originalCommitId // ignore: cast_nullable_to_non_nullable - as String, - body: body == freezed - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - line: line == freezed - ? _value.line - : line // ignore: cast_nullable_to_non_nullable - as int?, - )); - } -} - -/// @nodoc -abstract class _$$_ReviewCommentCopyWith<$Res> - implements $ReviewCommentCopyWith<$Res> { - factory _$$_ReviewCommentCopyWith( - _$_ReviewComment value, $Res Function(_$_ReviewComment) then) = - __$$_ReviewCommentCopyWithImpl<$Res>; - @override - $Res call({String path, String originalCommitId, String body, int? line}); -} - -/// @nodoc -class __$$_ReviewCommentCopyWithImpl<$Res> - extends _$ReviewCommentCopyWithImpl<$Res> - implements _$$_ReviewCommentCopyWith<$Res> { - __$$_ReviewCommentCopyWithImpl( - _$_ReviewComment _value, $Res Function(_$_ReviewComment) _then) - : super(_value, (v) => _then(v as _$_ReviewComment)); - - @override - _$_ReviewComment get _value => super._value as _$_ReviewComment; - - @override - $Res call({ - Object? path = freezed, - Object? originalCommitId = freezed, - Object? body = freezed, - Object? line = freezed, - }) { - return _then(_$_ReviewComment( - path: path == freezed - ? _value.path - : path // ignore: cast_nullable_to_non_nullable - as String, - originalCommitId: originalCommitId == freezed - ? _value.originalCommitId - : originalCommitId // ignore: cast_nullable_to_non_nullable - as String, - body: body == freezed - ? _value.body - : body // ignore: cast_nullable_to_non_nullable - as String, - line: line == freezed - ? _value.line - : line // ignore: cast_nullable_to_non_nullable - as int?, - )); - } -} - -/// @nodoc - -@JsonSerializable(fieldRename: FieldRename.snake) -class _$_ReviewComment implements _ReviewComment { - const _$_ReviewComment( - {required this.path, - required this.originalCommitId, - required this.body, - this.line}); - - factory _$_ReviewComment.fromJson(Map json) => - _$$_ReviewCommentFromJson(json); - - @override - final String path; - @override - final String originalCommitId; - @override - final String body; - @override - final int? line; - - @override - String toString() { - return 'ReviewComment(path: $path, originalCommitId: $originalCommitId, body: $body, line: $line)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_ReviewComment && - const DeepCollectionEquality().equals(other.path, path) && - const DeepCollectionEquality() - .equals(other.originalCommitId, originalCommitId) && - const DeepCollectionEquality().equals(other.body, body) && - const DeepCollectionEquality().equals(other.line, line)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(path), - const DeepCollectionEquality().hash(originalCommitId), - const DeepCollectionEquality().hash(body), - const DeepCollectionEquality().hash(line)); - - @JsonKey(ignore: true) - @override - _$$_ReviewCommentCopyWith<_$_ReviewComment> get copyWith => - __$$_ReviewCommentCopyWithImpl<_$_ReviewComment>(this, _$identity); - - @override - Map toJson() { - return _$$_ReviewCommentToJson(this); - } -} - -abstract class _ReviewComment implements ReviewComment { - const factory _ReviewComment( - {required final String path, - required final String originalCommitId, - required final String body, - final int? line}) = _$_ReviewComment; - - factory _ReviewComment.fromJson(Map json) = - _$_ReviewComment.fromJson; - - @override - String get path => throw _privateConstructorUsedError; - @override - String get originalCommitId => throw _privateConstructorUsedError; - @override - String get body => throw _privateConstructorUsedError; - @override - int? get line => throw _privateConstructorUsedError; - @override - @JsonKey(ignore: true) - _$$_ReviewCommentCopyWith<_$_ReviewComment> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/data/model/review_comment.g.dart b/lib/data/model/review_comment.g.dart deleted file mode 100644 index bb12a13..0000000 --- a/lib/data/model/review_comment.g.dart +++ /dev/null @@ -1,25 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -// ignore_for_file: non_constant_identifier_names - -part of 'review_comment.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$_ReviewComment _$$_ReviewCommentFromJson(Map json) => - _$_ReviewComment( - path: json['path'] as String, - originalCommitId: json['original_commit_id'] as String, - body: json['body'] as String, - line: json['line'] as int?, - ); - -Map _$$_ReviewCommentToJson(_$_ReviewComment instance) => - { - 'path': instance.path, - 'original_commit_id': instance.originalCommitId, - 'body': instance.body, - 'line': instance.line, - }; diff --git a/lib/data/parser/analyze_result_parser.dart b/lib/data/parser/analyze_result_parser.dart new file mode 100644 index 0000000..7637417 --- /dev/null +++ b/lib/data/parser/analyze_result_parser.dart @@ -0,0 +1,42 @@ +import 'package:elixir/data/model/analyze_result.dart'; +import 'package:path/path.dart' as p; + +/// `dart analyze --format=machine` +/// SEVERITY|TYPE|ERROR_CODE|FILE_PATH|LINE|COLUMN|LENGTH|ERROR_MESSAGE +/// +/// e.g.: +/// ERROR|COMPILE_TIME_ERROR|UNDEFINED_GETTER|/Users/blendthink/Documents/repositories/danger-plugin-dart-analyze/lib/src/models/dart_analyze_result.dart|25|17|4|The getter 'name' isn't defined for the type 'T'. +class AnalyzeResultParser { + const AnalyzeResultParser(); + + static final _regex = RegExp( + r'(INFO|WARNING|ERROR)\|(.+)\|(.+)\|(.+)\|(\d+)\|(\d+)\|(\d+)\|(.+)', + ); + + AnalyzeResult? parse(String logLine) { + final match = _regex.firstMatch(logLine); + if (match == null) { + return null; + } + + final severity = Severity.values.byName(match.group(1)!.toLowerCase()); + final type = match.group(2)!; + final errorCode = match.group(3)!; + final filePath = p.relative(match.group(4)!); + final line = int.parse(match.group(5)!); + final column = int.parse(match.group(6)!); + final length = int.parse(match.group(7)!); + final errorMessage = match.group(8)!; + + return AnalyzeResult( + severity: severity, + type: type, + errorCode: errorCode, + filePath: filePath, + line: line, + column: column, + length: length, + errorMessage: errorMessage, + ); + } +} diff --git a/lib/data/repository/dart.dart b/lib/data/repository/dart.dart index 32a5e1a..352ce24 100644 --- a/lib/data/repository/dart.dart +++ b/lib/data/repository/dart.dart @@ -1,31 +1,26 @@ -import 'dart:convert'; import 'dart:io'; -import 'package:elixir/data/model/analysis.dart'; +import 'package:elixir/data/model/analyze_result.dart'; +import 'package:elixir/data/parser/analyze_result_parser.dart'; import 'package:elixir/data/source/process.dart'; -class DartRepository { - final ProcessRunner _runner; +const _parser = AnalyzeResultParser(); +class DartRepository { const DartRepository({ ProcessRunner runner = const ProcessRunner('dart'), }) : _runner = runner; - Future analyze({ + final ProcessRunner _runner; + + Future> analyze({ required Directory dir, }) async { final result = await _runner.run( - ['analyze', '--format=json', dir.path], + ['analyze', '--format=machine', dir.path], ignoreError: true, ); - final jsonStart = result.indexOf('{'); - final jsonEnd = result.lastIndexOf('}'); - if (jsonStart == -1 || jsonEnd == -1) { - return AnalysisResults(version: 0, diagnostics: const []); - } - - final jsonContent = result.substring(jsonStart, jsonEnd + 1); - return AnalysisResults.fromJson(jsonDecode(jsonContent)); + return result.split('\n').map(_parser.parse).whereType(); } } diff --git a/lib/data/repository/git.dart b/lib/data/repository/git.dart index 1a60d69..381e77c 100644 --- a/lib/data/repository/git.dart +++ b/lib/data/repository/git.dart @@ -1,16 +1,18 @@ import 'package:elixir/data/source/process.dart'; class GitRepository { - final ProcessRunner _runner; - const GitRepository({ ProcessRunner runner = const ProcessRunner('git'), }) : _runner = runner; + final ProcessRunner _runner; + /// git log {base}..{head} --format=%H -n 1 -L {line},+1:{path} /// /// If there is no commit between {base} and {head}: '' - /// If a commit exists between {base} and {head}: e.g. '6d87458a8089ae5d025b087bfb9b0809d2060411' + /// + /// If a commit exists between {base} and {head}: e.g.: + /// '6d87458a8089ae5d025b087bfb9b0809d2060411' Future getLatestCommitId({ required String base, required String head, @@ -29,7 +31,9 @@ class GitRepository { final regex = RegExp(r'^([\d|a-z]{40})'); final match = regex.firstMatch(result); - if (match == null) return ''; + if (match == null) { + return ''; + } return match.group(1) ?? ''; } diff --git a/lib/data/repository/github.dart b/lib/data/repository/github.dart index 5cf1eeb..4271877 100644 --- a/lib/data/repository/github.dart +++ b/lib/data/repository/github.dart @@ -1,66 +1,74 @@ import 'dart:convert'; -import 'package:elixir/data/model/review_comment.dart'; -import 'package:elixir/data/source/process.dart'; +import 'package:elixir/data/model/comment.dart'; +import 'package:elixir/infra/client.dart'; +import 'package:elixir/infra/response/comment.dart'; class GitHubRepository { - final ProcessRunner _runner; - const GitHubRepository({ - ProcessRunner runner = const ProcessRunner('gh'), - }) : _runner = runner; + required GitHubClient client, + }) : _client = client; + + final GitHubClient _client; - /// gh api \ - /// -H "Accept: application/vnd.github.v3+json" \ - /// /repos/{repo}/pulls/{num}/comments - Future> getReviewComments({ + /// List review comments on a pull request + /// https://docs.github.com/en/rest/pulls/comments#list-review-comments-on-a-pull-request + Future> listReviewComments({ required String repo, required int num, }) async { - final result = await _runner.run([ - 'api', - '-H', - 'Accept: application/vnd.github.v3+json', - '/repos/$repo/pulls/$num/comments', - ]); - - return (jsonDecode(result) as List) - .map((json) => ReviewComment.fromJson(json)); + final result = await _client.getRequest( + path: 'repos/$repo/pulls/$num/comments', + ); + final json = jsonDecode(result) as Iterable; + return List.from( + json.map((e) => ReviewComment.fromJson(e as Map)), + ); } - /// gh api \ - /// --method POST \ - /// -H "Accept: application/vnd.github.v3+json" \ - /// /repos/{repo}/pulls/{num}/comments \ - /// -f body={body} \ - /// -f commit_id={commitId} \ - /// -f path={path} \ - /// -F line={line} - Future createReviewComment({ + /// Create a review for a pull request + /// https://docs.github.com/en/rest/pulls/reviews#create-a-review-for-a-pull-request + Future createReview({ required String repo, required int num, - required String body, - required String commitId, - required String path, - required int line, + required List comments, }) async { - final result = await _runner.run([ - 'api', - '--method', - 'POST', - '-H', - 'Accept: application/vnd.github.v3+json', - '/repos/$repo/pulls/$num/comments', - '-f', - 'body=$body', - '-f', - 'commit_id=$commitId', - '-f', - 'path=$path', - '-F', - 'line=$line', - ]); - - return ReviewComment.fromJson(jsonDecode(result)); + final data = _Data( + body: '', + event: 'COMMENT', + comments: comments, + ); + return _client.postRequest( + path: 'repos/$repo/pulls/$num/reviews', + data: jsonEncode(data), + ); } + + /// Delete a review comment for a pull request + /// https://docs.github.com/en/rest/pulls/comments#delete-a-review-comment-for-a-pull-request + Future deleteReviewComment({ + required String repo, + required int commentId, + }) async => + _client.deleteRequest( + path: 'repos/$repo/pulls/comments/$commentId', + ); +} + +class _Data { + _Data({ + required this.body, + required this.event, + required this.comments, + }); + + final String body; + final String event; + final List comments; + + Map toJson() => { + 'body': body, + 'event': event, + 'comments': comments, + }; } diff --git a/lib/data/source/process.dart b/lib/data/source/process.dart index ba6cf64..7c86eb2 100644 --- a/lib/data/source/process.dart +++ b/lib/data/source/process.dart @@ -3,10 +3,10 @@ import 'dart:io'; import 'package:elixir/util/log.dart'; class ProcessRunner { - final String _executable; - const ProcessRunner(this._executable); + final String _executable; + Future run( List args, { bool ignoreError = false, @@ -30,10 +30,10 @@ class ProcessRunner { } class ProcessException implements Exception { - final int exitCode; - const ProcessException(this.exitCode); + final int exitCode; + @override String toString() => 'failed with exit code $exitCode'; } diff --git a/lib/elixir.dart b/lib/elixir.dart index 7963324..7015756 100644 --- a/lib/elixir.dart +++ b/lib/elixir.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:elixir/cli/runner.dart'; - import 'package:elixir/util/log.dart'; Future run(List args) async { @@ -10,31 +9,41 @@ Future run(List args) async { await exitStatus.flushThenExit(); } -Future<_ExitStatus> _run(List args) async { +Future _run(List args) async { try { await ElixirCommandRunner().run(args); - return _ExitStatus.success; + return ExitStatus.success; } on UsageException catch (e) { log.w(e); - return _ExitStatus.usage; + return ExitStatus.usage; } on Exception catch (e) { log.e(e); - return _ExitStatus.error; + return ExitStatus.error; } } -enum _ExitStatus { - success(0), - usage(64), - error(1), - ; - - final int code; - - const _ExitStatus(this.code); +enum ExitStatus { + success, + usage, + error, +} - Future flushThenExit() async { - return Future.wait([stdout.close(), stderr.close()]) - .then((_) => exit(code)); +extension ExitStatusExt on ExitStatus { + int get code { + switch (this) { + case ExitStatus.success: + return 0; + case ExitStatus.usage: + return 64; + case ExitStatus.error: + return 1; + } } + + Future flushThenExit() async => Future.wait( + [ + stdout.close(), + stderr.close(), + ], + ).then((_) => exit(code)); } diff --git a/lib/extension/iterable.dart b/lib/extension/iterable.dart new file mode 100644 index 0000000..62392db --- /dev/null +++ b/lib/extension/iterable.dart @@ -0,0 +1,9 @@ +extension IterableGroupBy on Iterable { + Map> groupBy(T Function(S) key) { + final map = >{}; + for (final element in this) { + (map[key(element)] ??= []).add(element); + } + return map; + } +} diff --git a/lib/gen/pubspec.dart b/lib/gen/pubspec.dart deleted file mode 100644 index 6d6eee4..0000000 --- a/lib/gen/pubspec.dart +++ /dev/null @@ -1,34 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -import 'package:pubspec_parse/pubspec_parse.dart'; - -final pubspec = Pubspec.parse("""name: elixir -description: Tool for commenting `dart analyze` results to GitHub PullRequest. -version: 1.0.6 -homepage: https://github.com/blendthink/elixir -repository: https://github.com/blendthink/elixir -documentation: https://github.com/blendthink/elixir -issue_tracker: https://github.com/blendthink/elixir/issues - -executables: - elixir: - -environment: - sdk: '>=2.17.5 <3.0.0' - -dependencies: - args: ^2.3.1 - crypto: ^3.0.2 - freezed_annotation: ^2.0.3 - json_serializable: ^6.2.0 - path: ^1.8.2 - pubspec_parse: ^1.2.0 - -dev_dependencies: - build_runner: ^2.1.11 - freezed: ^2.0.3+1 - lints: ^2.0.0 - mockito: ^5.2.0 - pubspec_builder: ^1.0.0 - test: ^1.16.0 -"""); diff --git a/lib/gen/version.gen.dart b/lib/gen/version.gen.dart new file mode 100644 index 0000000..cbc44b0 --- /dev/null +++ b/lib/gen/version.gen.dart @@ -0,0 +1,2 @@ +/// DO NOT MODIFY BY HAND, Generated by version_gen +String packageVersion = '2.0.0'; diff --git a/lib/infra/client.dart b/lib/infra/client.dart new file mode 100644 index 0000000..1373d8d --- /dev/null +++ b/lib/infra/client.dart @@ -0,0 +1,80 @@ +import 'package:elixir/data/model/exception/github.dart'; +import 'package:http/http.dart' + show BaseClient, Client, BaseRequest, StreamedResponse; + +class GitHubClient extends BaseClient { + GitHubClient({ + required Client client, + required String token, + }) : _client = client, + _token = token; + + final Client _client; + final String _token; + + final _authority = 'api.github.com'; + + @override + Future send(BaseRequest request) { + request.headers['Accept'] = 'application/vnd.github+json'; + request.headers['Authorization'] = 'token $_token'; + return _client.send(request); + } + + Future getRequest({ + required String path, + }) async { + final url = Uri.https(_authority, path); + final response = await super.get(url); + _checkStatusCode(response.statusCode); + return response.body; + } + + Future postRequest({ + required String path, + required String data, + }) async { + final url = Uri.https(_authority, path); + final response = await super.post(url, body: data); + _checkStatusCode(response.statusCode); + return response.body; + } + + Future deleteRequest({ + required String path, + }) async { + final url = Uri.https(_authority, path); + final response = await super.delete(url); + _checkStatusCode(response.statusCode); + } + + void _checkStatusCode(int statusCode) { + switch (statusCode) { + case 200: + case 201: + case 204: + return; + case 400: + throw const BadRequest(); + case 401: + throw const RequiresAuthentication(); + case 403: + throw const Forbidden(); + case 404: + throw const NotFound(); + case 422: + throw const ValidationFailed(); + case 429: + throw const TooManyRequests(); + case 500: + throw const InternalError(); + case 503: + throw const ServiceUnavailable(); + default: + throw const Unknown(); + } + } + + @override + void close() => _client.close(); +} diff --git a/lib/infra/response/comment.dart b/lib/infra/response/comment.dart new file mode 100644 index 0000000..00ac1f0 --- /dev/null +++ b/lib/infra/response/comment.dart @@ -0,0 +1,14 @@ +class ReviewComment { + ReviewComment({ + required this.id, + required this.body, + }); + + factory ReviewComment.fromJson(Map map) => ReviewComment( + id: map['id'] as int, + body: map['body'] as String, + ); + + final int id; + final String body; +} diff --git a/lib/usecase/comment_indicates.dart b/lib/usecase/comment_indicates.dart index 9e0174e..096cb8f 100644 --- a/lib/usecase/comment_indicates.dart +++ b/lib/usecase/comment_indicates.dart @@ -1,35 +1,72 @@ +import 'package:elixir/data/keys.dart'; +import 'package:elixir/data/model/comment.dart'; import 'package:elixir/data/model/indicate.dart'; -import 'package:elixir/data/model/review_comment.dart'; import 'package:elixir/data/repository/github.dart'; +import 'package:elixir/extension/iterable.dart'; +import 'package:meta/meta.dart'; class CommentIndicatesUseCase { - final GitHubRepository _gitHubRepository; - const CommentIndicatesUseCase({ - GitHubRepository gitHubRepository = const GitHubRepository(), + required GitHubRepository gitHubRepository, }) : _gitHubRepository = gitHubRepository; - Future> call({ + final GitHubRepository _gitHubRepository; + + Future call({ required String repo, required int num, required Iterable indicates, }) async { - final comments = []; - - // TODO: use https://docs.github.com/ja/rest/pulls/reviews#create-a-review-for-a-pull-request - for (final indicate in indicates) { - final comment = await _gitHubRepository.createReviewComment( - repo: repo, - num: num, - body: indicate.body, - commitId: indicate.commitId, + final commentKeyGroup = indicates.groupBy<_CommentKey>( + (indicate) => _CommentKey( path: indicate.path, line: indicate.line, + ), + ); + + final comments = commentKeyGroup.entries.map((entry) { + final key = entry.key; + final value = entry.value; + final body = ''' + + + +${value.map((e) => e.message).join('\n')} + +
'''; + return Comment( + path: key.path, + line: key.line, + body: body, ); - comments.add(comment); - await Future.delayed(Duration(seconds: 10)); - } + }).toList(); - return comments; + return _gitHubRepository.createReview( + repo: repo, + num: num, + comments: comments, + ); } } + +@immutable +class _CommentKey { + const _CommentKey({ + required this.path, + required this.line, + }); + + final String path; + final int line; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is _CommentKey && + runtimeType == other.runtimeType && + path == other.path && + line == other.line; + + @override + int get hashCode => path.hashCode ^ line.hashCode; +} diff --git a/lib/usecase/delete_previous_comments.dart b/lib/usecase/delete_previous_comments.dart new file mode 100644 index 0000000..a07befc --- /dev/null +++ b/lib/usecase/delete_previous_comments.dart @@ -0,0 +1,30 @@ +import 'package:elixir/data/keys.dart'; +import 'package:elixir/data/repository/github.dart'; + +class DeletePreviousCommentsUseCase { + const DeletePreviousCommentsUseCase({ + required GitHubRepository gitHubRepository, + }) : _gitHubRepository = gitHubRepository; + + final GitHubRepository _gitHubRepository; + + Future call({ + required String repo, + required int num, + }) async { + final previousComments = await _gitHubRepository.listReviewComments( + repo: repo, + num: num, + ); + + final filteredPreviousComments = + previousComments.where((comment) => comment.body.contains(elixirKey)); + + for (final comment in filteredPreviousComments) { + await _gitHubRepository.deleteReviewComment( + repo: repo, + commentId: comment.id, + ); + } + } +} diff --git a/lib/usecase/filter_indicates.dart b/lib/usecase/filter_indicates.dart deleted file mode 100644 index 54af283..0000000 --- a/lib/usecase/filter_indicates.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:elixir/data/model/indicate.dart'; -import 'package:elixir/data/repository/github.dart'; - -class FilterIndicatesUseCase { - final GitHubRepository _gitHubRepository; - - const FilterIndicatesUseCase({ - GitHubRepository gitHubRepository = const GitHubRepository(), - }) : _gitHubRepository = gitHubRepository; - - Future> call({ - required String repo, - required int num, - required Iterable indicates, - }) async { - final comments = - await _gitHubRepository.getReviewComments(repo: repo, num: num); - - final elixirComments = comments - .where((comment) => comment.body.contains('generatedBy: Elixir')); - - return indicates.where((indicate) { - // If commitId, path, line, and body are all the same, then it is excluded from the list. - return !elixirComments.any((comment) => - comment.originalCommitId == indicate.commitId && - comment.path == indicate.path && - comment.line == indicate.line && - comment.body.contains(indicate.diagHash)); - }); - } -} diff --git a/lib/usecase/get_indicates.dart b/lib/usecase/get_indicates.dart index 26ff0f5..e49dcc6 100644 --- a/lib/usecase/get_indicates.dart +++ b/lib/usecase/get_indicates.dart @@ -1,90 +1,75 @@ -import 'dart:convert'; import 'dart:io'; -import 'package:crypto/crypto.dart'; -import 'package:path/path.dart' as p; -import 'package:elixir/data/model/analysis.dart'; +import 'package:elixir/data/model/analyze_result.dart'; import 'package:elixir/data/model/indicate.dart'; import 'package:elixir/data/repository/dart.dart'; - import 'package:elixir/data/repository/git.dart'; class GetIndicatesUseCase { - final DartRepository _dartRepository; - final GitRepository _gitRepository; - const GetIndicatesUseCase({ DartRepository dartRepository = const DartRepository(), GitRepository gitRepository = const GitRepository(), }) : _dartRepository = dartRepository, _gitRepository = gitRepository; + final DartRepository _dartRepository; + final GitRepository _gitRepository; + Future> call({ required Directory dir, required String base, required String head, }) async { - final result = await _dartRepository.analyze(dir: dir); - - final indicates = await Future.wait(result.diagnostics.map((diag) async { - // If severity is none, then it is excluded from the list. - if (diag.severity == DiagnosticSeverity.none) return null; + final results = await _dartRepository.analyze(dir: dir); - final body = diag.body; - final path = p.relative(diag.location.file); - final line = diag.location.range.start.line; + final indicates = await Future.wait( + results.map( + (result) async { + final message = result.message; + final path = result.filePath; + final line = result.line; - final commitId = await _gitRepository.getLatestCommitId( - base: base, - head: head, - path: path, - line: line, - ); + final commitId = await _gitRepository.getLatestCommitId( + base: base, + head: head, + path: path, + line: line, + ); - // If there are no commits between {base} and {head}, then the commit id is empty. - // In this case, it is excluded from the list. - if (commitId.isEmpty) return null; + // If no diffs between {base} and {head}, then the commit id is empty. + // In this case, it is excluded from the list. + if (commitId.isEmpty) { + return null; + } - return Indicate( - diagHash: diag.sha, - body: body, - commitId: commitId, - path: path, - line: line, - ); - })); + return Indicate( + message: message, + path: path, + line: line, + ); + }, + ), + ); return indicates.whereType(); } } -extension DiagnosticExt on Diagnostic { - String get _title { +extension _AnalyzeResultExt on AnalyzeResult { + String get _emoji { switch (severity) { - case DiagnosticSeverity.none: - return ':eyes: **None**'; - case DiagnosticSeverity.info: - return ':speech_balloon: **Info**'; - case DiagnosticSeverity.warning: - return ':warning: **Warning**'; - case DiagnosticSeverity.error: - return ':rotating_light: **Error**'; + case Severity.info: + return 'speech_balloon'; + case Severity.warning: + return 'warning'; + case Severity.error: + return 'rotating_light'; } } - String get sha { - final json = jsonEncode(toJson()); - return sha256.convert(utf8.encode(json)).toString(); - } - - String get body => ''' - -> $_title `$code` -> $problemMessage -

- Generated by :pill: Elixir -

'''; + String get message => ''' + + :$_emoji: + $errorMessage + '''; } diff --git a/pubspec.lock b/pubspec.lock index 91eca0b..79dd56b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "41.0.0" + version: "39.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "4.2.0" + version: "4.0.0" args: dependency: "direct main" description: @@ -126,9 +126,9 @@ packages: name: coverage url: "https://pub.dartlang.org" source: hosted - version: "1.4.0" + version: "1.2.0" crypto: - dependency: "direct main" + dependency: transitive description: name: crypto url: "https://pub.dartlang.org" @@ -155,20 +155,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" - freezed: - dependency: "direct dev" - description: - name: freezed - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0+1" - freezed_annotation: - dependency: "direct main" - description: - name: freezed_annotation - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" frontend_server_client: dependency: transitive description: @@ -182,7 +168,7 @@ packages: name: glob url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.0.2" graphs: dependency: transitive description: @@ -190,6 +176,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + http: + dependency: "direct main" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.5" http_multi_server: dependency: transitive description: @@ -217,7 +210,7 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.4" + version: "0.6.3" json_annotation: dependency: transitive description: @@ -225,20 +218,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.6.0" - json_serializable: - dependency: "direct main" - description: - name: json_serializable - url: "https://pub.dartlang.org" - source: hosted - version: "6.3.1" - lints: - dependency: "direct dev" - description: - name: lints - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" logging: dependency: transitive description: @@ -254,7 +233,7 @@ packages: source: hosted version: "0.12.12" meta: - dependency: transitive + dependency: "direct main" description: name: meta url: "https://pub.dartlang.org" @@ -273,7 +252,7 @@ packages: name: mockito url: "https://pub.dartlang.org" source: hosted - version: "5.3.0" + version: "5.2.0" node_preamble: dependency: transitive description: @@ -309,15 +288,8 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" - pubspec_builder: - dependency: "direct dev" - description: - name: pubspec_builder - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" pubspec_parse: - dependency: "direct main" + dependency: transitive description: name: pubspec_parse url: "https://pub.dartlang.org" @@ -329,7 +301,7 @@ packages: name: shelf url: "https://pub.dartlang.org" source: hosted - version: "1.3.1" + version: "1.2.0" shelf_packages_handler: dependency: transitive description: @@ -358,13 +330,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.2" - source_helper: - dependency: transitive - description: - name: source_helper - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.2" source_map_stack_trace: dependency: transitive description: @@ -456,13 +421,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + version_gen: + dependency: "direct dev" + description: + name: version_gen + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" vm_service: dependency: transitive description: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "9.0.0" + version: "8.2.2" watcher: dependency: transitive description: @@ -492,4 +464,4 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.17.5 <3.0.0" + dart: ">=2.15.0 <3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index f6e43a6..154eae5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: elixir description: Tool for commenting `dart analyze` results to GitHub PullRequest. -version: 1.0.6 +version: 2.0.0 homepage: https://github.com/blendthink/elixir repository: https://github.com/blendthink/elixir documentation: https://github.com/blendthink/elixir @@ -10,20 +10,16 @@ executables: elixir: environment: - sdk: '>=2.17.5 <3.0.0' + sdk: '>=2.15.0 <3.0.0' dependencies: args: ^2.3.1 - crypto: ^3.0.2 - freezed_annotation: ^2.0.3 - json_serializable: ^6.2.0 + http: ^0.13.5 + meta: ^1.8.0 path: ^1.8.2 - pubspec_parse: ^1.2.0 dev_dependencies: - build_runner: ^2.1.11 - freezed: ^2.0.3+1 - lints: ^2.0.0 - mockito: ^5.2.0 - pubspec_builder: ^1.0.0 - test: ^1.16.0 + build_runner: ^2.2.0 + mockito: 5.2.0 + test: ^1.21.4 + version_gen: ^1.0.1 diff --git a/test/data/parser/analyze_result_parser_test.dart b/test/data/parser/analyze_result_parser_test.dart new file mode 100644 index 0000000..40ff9e6 --- /dev/null +++ b/test/data/parser/analyze_result_parser_test.dart @@ -0,0 +1,61 @@ +import 'package:elixir/data/model/analyze_result.dart'; +import 'package:elixir/data/parser/analyze_result_parser.dart'; +import 'package:path/path.dart'; +import 'package:test/test.dart'; + +void main() { + const parser = AnalyzeResultParser(); + test('error', () { + const expected = AnalyzeResult( + severity: Severity.error, + type: 'SYNTACTIC_ERROR', + errorCode: 'EXPECTED_TOKEN', + filePath: 'lib/main.dart', + line: 5, + column: 23, + length: 1, + errorMessage: """Expected to find ';'.""", + ); + final actual = parser.parse( + '''ERROR|SYNTACTIC_ERROR|EXPECTED_TOKEN|$current/lib/main.dart|5|23|1|Expected to find ';'.''', + ); + expect(actual, expected); + }); + test('warning', () { + const expected = AnalyzeResult( + severity: Severity.warning, + type: 'STATIC_WARNING', + errorCode: 'UNSUPPORTED_OPTION_WITH_LEGAL_VALUE', + filePath: 'analysis_options.yaml', + line: 4, + column: 3, + length: 3, + errorMessage: + """The option 'foo' isn't supported by 'linter'. Try using the only supported option: ''rules''.""", + ); + final actual = parser.parse( + '''WARNING|STATIC_WARNING|UNSUPPORTED_OPTION_WITH_LEGAL_VALUE|$current/analysis_options.yaml|4|3|3|The option 'foo' isn't supported by 'linter'. Try using the only supported option: ''rules''.''', + ); + expect(actual, expected); + }); + test('info', () { + const expected = AnalyzeResult( + severity: Severity.info, + type: 'HINT', + errorCode: 'UNUSED_LOCAL_VARIABLE', + filePath: 'lib/main.dart', + line: 4, + column: 9, + length: 3, + errorMessage: """The value of the local variable 'foo' isn't used.""", + ); + final actual = parser.parse( + '''INFO|HINT|UNUSED_LOCAL_VARIABLE|$current/lib/main.dart|4|9|3|The value of the local variable 'foo' isn't used.''', + ); + expect(actual, expected); + }); + test('null', () { + final actual = parser.parse(''); + expect(actual, null); + }); +} diff --git a/test/data/repository/dart_test.dart b/test/data/repository/dart_test.dart index 387c1a2..2fcebcb 100644 --- a/test/data/repository/dart_test.dart +++ b/test/data/repository/dart_test.dart @@ -1,10 +1,11 @@ import 'dart:io'; -import 'package:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; -import 'package:elixir/data/model/analysis.dart'; +import 'package:elixir/data/model/analyze_result.dart'; import 'package:elixir/data/repository/dart.dart'; import 'package:elixir/data/source/process.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:path/path.dart'; import 'package:test/test.dart'; import 'dart_test.mocks.dart'; @@ -15,110 +16,76 @@ Future main() async { final repository = DartRepository(runner: runner); final dir = Directory('./'); - test('Diagnostics is empty.', () async { - when(runner.run(any, ignoreError: anyNamed('ignoreError'))).thenAnswer((_) async => ''' -Analyzing flutter-mobile-lints... 0.8s -No issues found!'''); - - final expected = AnalysisResults( - version: 0, - diagnostics: [], - ); - + test('Diagnostic is empty.', () async { + when(runner.run(any, ignoreError: anyNamed('ignoreError'))) + .thenAnswer((_) async => ''); + const expected = Iterable.empty(); final actual = await repository.analyze(dir: dir); expect(actual, expected); }); test('One diagnostic exists.', () async { - when(runner.run(any, ignoreError: anyNamed('ignoreError'))).thenAnswer((_) async => ''' -Analyzing danger-flutter-lint-demo... 1.7s -{"version":1,"diagnostics":[{"code":"argument_type_not_assignable","severity":"ERROR","type":"COMPILE_TIME_ERROR","location":{"file":"/Users/blendthink/Documents/repositories/danger-flutter-lint-demo/lib/main.dart","range":{"start":{"offset":305,"line":14,"column":14},"end":{"offset":309,"line":14,"column":18}}},"problemMessage":"The argument type 'Null' can't be assigned to the parameter type 'String'.","documentation":"https://dart.dev/diagnostics/argument_type_not_assignable"}]}'''); - - final expected = AnalysisResults( - version: 1, - diagnostics: [ - Diagnostic( - code: 'argument_type_not_assignable', - severity: DiagnosticSeverity.error, - type: DiagnosticType.compileTimeError, - location: Location( - file: - '/Users/blendthink/Documents/repositories/danger-flutter-lint-demo/lib/main.dart', - range: Range( - start: Position(offset: 305, line: 14, column: 14), - end: Position(offset: 309, line: 14, column: 18), - ), - ), - problemMessage: - "The argument type 'Null' can't be assigned to the parameter type 'String'.", - documentation: - 'https://dart.dev/diagnostics/argument_type_not_assignable', - ), - ], + when(runner.run(any, ignoreError: anyNamed('ignoreError'))).thenAnswer( + (_) async => + '''ERROR|SYNTACTIC_ERROR|EXPECTED_TOKEN|$current/lib/main.dart|5|23|1|Expected to find ';'.''', ); - + const expected = [ + AnalyzeResult( + severity: Severity.error, + type: 'SYNTACTIC_ERROR', + errorCode: 'EXPECTED_TOKEN', + filePath: 'lib/main.dart', + line: 5, + column: 23, + length: 1, + errorMessage: """Expected to find ';'.""", + ), + ]; final actual = await repository.analyze(dir: dir); expect(actual, expected); }); test('Multiple diagnostics exist.', () async { - when(runner.run(any, ignoreError: anyNamed('ignoreError'))).thenAnswer((_) async => ''' -Analyzing danger-flutter-lint-demo... 1.7s -{"version":1,"diagnostics":[{"code":"argument_type_not_assignable","severity":"ERROR","type":"COMPILE_TIME_ERROR","location":{"file":"/Users/blendthink/Documents/repositories/danger-flutter-lint-demo/lib/main.dart","range":{"start":{"offset":305,"line":14,"column":14},"end":{"offset":309,"line":14,"column":18}}},"problemMessage":"The argument type 'Null' can't be assigned to the parameter type 'String'.","documentation":"https://dart.dev/diagnostics/argument_type_not_assignable"},{"code":"unrecognized_error_code","severity":"WARNING","type":"STATIC_WARNING","location":{"file":"/Users/blendthink/Documents/repositories/danger-flutter-lint-demo/analysis_options.yaml","range":{"start":{"offset":121,"line":5,"column":5},"end":{"offset":125,"line":5,"column":9}}},"problemMessage":"'demo' isn't a recognized error code."},{"code":"always_put_required_named_parameters_first","severity":"INFO","type":"LINT","location":{"file":"/Users/blendthink/Documents/repositories/danger-flutter-lint-demo/lib/main.dart","range":{"start":{"offset":1094,"line":33,"column":45},"end":{"offset":1099,"line":33,"column":50}}},"problemMessage":"Put required named parameters first.","documentation":"https://dart-lang.github.io/linter/lints/always_put_required_named_parameters_first.html"}]}'''); - - final expected = AnalysisResults( - version: 1, - diagnostics: [ - Diagnostic( - code: 'argument_type_not_assignable', - severity: DiagnosticSeverity.error, - type: DiagnosticType.compileTimeError, - location: Location( - file: - '/Users/blendthink/Documents/repositories/danger-flutter-lint-demo/lib/main.dart', - range: Range( - start: Position(offset: 305, line: 14, column: 14), - end: Position(offset: 309, line: 14, column: 18), - ), - ), - problemMessage: - "The argument type 'Null' can't be assigned to the parameter type 'String'.", - documentation: - 'https://dart.dev/diagnostics/argument_type_not_assignable', - ), - Diagnostic( - code: 'unrecognized_error_code', - severity: DiagnosticSeverity.warning, - type: DiagnosticType.staticWarning, - location: Location( - file: - '/Users/blendthink/Documents/repositories/danger-flutter-lint-demo/analysis_options.yaml', - range: Range( - start: Position(offset: 121, line: 5, column: 5), - end: Position(offset: 125, line: 5, column: 9), - ), - ), - problemMessage: "'demo' isn't a recognized error code.", - ), - Diagnostic( - code: 'always_put_required_named_parameters_first', - severity: DiagnosticSeverity.info, - type: DiagnosticType.lint, - location: Location( - file: - '/Users/blendthink/Documents/repositories/danger-flutter-lint-demo/lib/main.dart', - range: Range( - start: Position(offset: 1094, line: 33, column: 45), - end: Position(offset: 1099, line: 33, column: 50), - ), - ), - problemMessage: 'Put required named parameters first.', - documentation: - 'https://dart-lang.github.io/linter/lints/always_put_required_named_parameters_first.html', - ), - ], + when(runner.run(any, ignoreError: anyNamed('ignoreError'))).thenAnswer( + (_) async => ''' +ERROR|SYNTACTIC_ERROR|EXPECTED_TOKEN|$current/lib/main.dart|5|23|1|Expected to find ';'. +WARNING|STATIC_WARNING|UNSUPPORTED_OPTION_WITH_LEGAL_VALUE|$current/analysis_options.yaml|4|3|3|The option 'foo' isn't supported by 'linter'. Try using the only supported option: ''rules''. +INFO|HINT|UNUSED_LOCAL_VARIABLE|$current/lib/main.dart|4|9|3|The value of the local variable 'foo' isn't used.''', ); + const expected = [ + AnalyzeResult( + severity: Severity.error, + type: 'SYNTACTIC_ERROR', + errorCode: 'EXPECTED_TOKEN', + filePath: 'lib/main.dart', + line: 5, + column: 23, + length: 1, + errorMessage: """Expected to find ';'.""", + ), + AnalyzeResult( + severity: Severity.warning, + type: 'STATIC_WARNING', + errorCode: 'UNSUPPORTED_OPTION_WITH_LEGAL_VALUE', + filePath: 'analysis_options.yaml', + line: 4, + column: 3, + length: 3, + errorMessage: + """The option 'foo' isn't supported by 'linter'. Try using the only supported option: ''rules''.""", + ), + AnalyzeResult( + severity: Severity.info, + type: 'HINT', + errorCode: 'UNUSED_LOCAL_VARIABLE', + filePath: 'lib/main.dart', + line: 4, + column: 9, + length: 3, + errorMessage: """The value of the local variable 'foo' isn't used.""", + ), + ]; final actual = await repository.analyze(dir: dir); expect(actual, expected); }); diff --git a/test/data/repository/github_test.dart b/test/data/repository/github_test.dart index 3f77ad9..65766e7 100644 --- a/test/data/repository/github_test.dart +++ b/test/data/repository/github_test.dart @@ -1,82 +1,112 @@ +import 'package:elixir/data/model/exception/github.dart'; +import 'package:elixir/data/repository/github.dart'; +import 'package:elixir/infra/client.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:elixir/data/model/review_comment.dart'; -import 'package:elixir/data/repository/github.dart'; -import 'package:elixir/data/source/process.dart'; import 'package:test/test.dart'; import 'github_test.mocks.dart'; -@GenerateMocks([ProcessRunner]) +@GenerateMocks([GitHubClient]) Future main() async { - final runner = MockProcessRunner(); - final repository = GitHubRepository(runner: runner); - - group('getReviewComments', () { - test('One review comment exists.', () async { - when(runner.run(any)).thenAnswer((_) async => - '''[{"url":"https://api.github.com/repos/octocat/Hello-World/pulls/comments/1","pull_request_review_id":42,"id":10,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDEw","diff_hunk":"@@ -16,33 +16,40 @@ public class Connection : IConnection...","path":"file1.txt","position":1,"original_position":4,"commit_id":"6dcb09b5b57875f334f61aebed695e2e4193db5e","original_commit_id":"9c48853fa3dc5c1c3d6f1f1cd1f2743e72652840","in_reply_to_id":8,"user":{"login":"octocat","id":1,"node_id":"MDQ6VXNlcjE=","avatar_url":"https://github.com/images/error/octocat_happy.gif","gravatar_id":"","url":"https://api.github.com/users/octocat","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","repos_url":"https://api.github.com/users/octocat/repos","events_url":"https://api.github.com/users/octocat/events{/privacy}","received_events_url":"https://api.github.com/users/octocat/received_events","type":"User","site_admin":false},"body":"Great stuff!","created_at":"2011-04-14T16:00:49Z","updated_at":"2011-04-14T16:00:49Z","html_url":"https://github.com/octocat/Hello-World/pull/1#discussion-diff-1","pull_request_url":"https://api.github.com/repos/octocat/Hello-World/pulls/1","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/octocat/Hello-World/pulls/comments/1"},"html":{"href":"https://github.com/octocat/Hello-World/pull/1#discussion-diff-1"},"pull_request":{"href":"https://api.github.com/repos/octocat/Hello-World/pulls/1"}},"start_line":1,"original_start_line":1,"start_side":"RIGHT","line":2,"original_line":2,"side":"RIGHT"}]'''); - - final expected = [ - ReviewComment( - path: 'file1.txt', - originalCommitId: '9c48853fa3dc5c1c3d6f1f1cd1f2743e72652840', - body: 'Great stuff!', - line: 2, - ), - ]; - - final actual = await repository.getReviewComments( - repo: 'octocat/Hello-World', num: 1); - expect(actual, expected); - }); - - test('Throw ProcessException.', () async { - when(runner.run(any)).thenThrow(const ProcessException(1)); - expect( - () async => await repository.getReviewComments( - repo: 'octocat/Hello-World', num: 1), - throwsA(TypeMatcher()), - ); - }); - }); - - group('createReviewComment', () { - test('Success.', () async { - when(runner.run(any)).thenAnswer((_) async => - '''{"url":"https://api.github.com/repos/octocat/Hello-World/pulls/comments/1","pull_request_review_id":42,"id":10,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDEw","diff_hunk":"@@ -16,33 +16,40 @@ public class Connection : IConnection...","path":"file1.txt","position":1,"original_position":4,"commit_id":"6dcb09b5b57875f334f61aebed695e2e4193db5e","original_commit_id":"9c48853fa3dc5c1c3d6f1f1cd1f2743e72652840","in_reply_to_id":8,"user":{"login":"octocat","id":1,"node_id":"MDQ6VXNlcjE=","avatar_url":"https://github.com/images/error/octocat_happy.gif","gravatar_id":"","url":"https://api.github.com/users/octocat","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","repos_url":"https://api.github.com/users/octocat/repos","events_url":"https://api.github.com/users/octocat/events{/privacy}","received_events_url":"https://api.github.com/users/octocat/received_events","type":"User","site_admin":false},"body":"Great stuff!","created_at":"2011-04-14T16:00:49Z","updated_at":"2011-04-14T16:00:49Z","html_url":"https://github.com/octocat/Hello-World/pull/1#discussion-diff-1","pull_request_url":"https://api.github.com/repos/octocat/Hello-World/pulls/1","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/octocat/Hello-World/pulls/comments/1"},"html":{"href":"https://github.com/octocat/Hello-World/pull/1#discussion-diff-1"},"pull_request":{"href":"https://api.github.com/repos/octocat/Hello-World/pulls/1"}},"start_line":1,"original_start_line":1,"start_side":"RIGHT","line":2,"original_line":2,"side":"RIGHT"}'''); - - final expected = ReviewComment( - path: 'file1.txt', - originalCommitId: '9c48853fa3dc5c1c3d6f1f1cd1f2743e72652840', - body: 'Great stuff!', - line: 2, - ); + final client = MockGitHubClient(); + final repository = GitHubRepository(client: client); - final actual = await repository.createReviewComment( - repo: 'octocat/Hello-World', - num: 1, - body: 'Great stuff!', - commitId: '9c48853fa3dc5c1c3d6f1f1cd1f2743e72652840', - path: 'file1.txt', - line: 2, - ); - expect(actual, expected); - }); - - test('Throw ProcessException.', () async { - when(runner.run(any)).thenThrow(const ProcessException(1)); - expect( - () async => await repository.createReviewComment( + group('createReview', () { + group('Success', () { + test('One review comment exists.', () async { + const expected = ''' +{ + "id": 80, + "node_id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3ODA=", + "user": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "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", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "body": "This is close to perfect! Please address the suggested inline change.", + "state": "CHANGES_REQUESTED", + "html_url": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80", + "pull_request_url": "https://api.github.com/repos/octocat/Hello-World/pulls/12", + "_links": { + "html": { + "href": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80" + }, + "pull_request": { + "href": "https://api.github.com/repos/octocat/Hello-World/pulls/12" + } + }, + "submitted_at": "2019-11-17T17:43:43Z", + "commit_id": "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091", + "author_association": "COLLABORATOR" +}'''; + when(client.postRequest(path: anyNamed('path'), data: anyNamed('data'))) + .thenAnswer( + (_) async => expected, + ); + final actual = await repository.createReview( repo: 'octocat/Hello-World', - num: 1, - body: 'Great stuff!', - commitId: '9c48853fa3dc5c1c3d6f1f1cd1f2743e72652840', - path: 'file1.txt', - line: 2, - ), - throwsA(TypeMatcher()), - ); + num: 12, + comments: [], + ); + expect(actual, expected); + }); + }); + group('Failure', () { + test('Forbidden.', () async { + const expected = Forbidden(); + when(client.postRequest(path: anyNamed('path'), data: anyNamed('data'))) + .thenThrow(expected); + expect( + () => repository.createReview( + repo: 'octocat/Hello-World', + num: 12, + comments: [], + ), + throwsA(expected), + ); + }); + test('Validation Failed.', () async { + const expected = ValidationFailed(); + when(client.postRequest(path: anyNamed('path'), data: anyNamed('data'))) + .thenThrow(expected); + expect( + () => repository.createReview( + repo: 'octocat/Hello-World', + num: 12, + comments: [], + ), + throwsA(expected), + ); + }); + test('Unknown.', () async { + const expected = Unknown(); + when(client.postRequest(path: anyNamed('path'), data: anyNamed('data'))) + .thenThrow(expected); + expect( + () => repository.createReview( + repo: 'octocat/Hello-World', + num: 12, + comments: [], + ), + throwsA(expected), + ); + }); }); }); } diff --git a/test/data/repository/github_test.mocks.dart b/test/data/repository/github_test.mocks.dart index 86118f6..ec52ff3 100644 --- a/test/data/repository/github_test.mocks.dart +++ b/test/data/repository/github_test.mocks.dart @@ -2,9 +2,12 @@ // in elixir/test/data/repository/github_test.dart. // Do not manually edit this file. -import 'dart:async' as _i3; +import 'dart:async' as _i4; +import 'dart:convert' as _i5; +import 'dart:typed_data' as _i6; -import 'package:elixir/data/source/process.dart' as _i2; +import 'package:elixir/infra/client.dart' as _i3; +import 'package:http/http.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: type=lint @@ -17,17 +20,92 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types -/// A class which mocks [ProcessRunner]. +class _FakeStreamedResponse_0 extends _i1.Fake implements _i2.StreamedResponse { +} + +class _FakeResponse_1 extends _i1.Fake implements _i2.Response {} + +/// A class which mocks [GitHubClient]. /// /// See the documentation for Mockito's code generation for more information. -class MockProcessRunner extends _i1.Mock implements _i2.ProcessRunner { - MockProcessRunner() { +class MockGitHubClient extends _i1.Mock implements _i3.GitHubClient { + MockGitHubClient() { _i1.throwOnMissingStub(this); } @override - _i3.Future run(List? args, {bool? ignoreError = false}) => + _i4.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => + (super.noSuchMethod(Invocation.method(#send, [request]), + returnValue: + Future<_i2.StreamedResponse>.value(_FakeStreamedResponse_0())) + as _i4.Future<_i2.StreamedResponse>); + @override + _i4.Future postRequest({String? path, String? data}) => + (super.noSuchMethod( + Invocation.method(#postRequest, [], {#path: path, #data: data}), + returnValue: Future.value('')) as _i4.Future); + @override + void close() => super.noSuchMethod(Invocation.method(#close, []), + returnValueForMissingStub: null); + @override + _i4.Future<_i2.Response> head(Uri? url, {Map? headers}) => + (super.noSuchMethod(Invocation.method(#head, [url], {#headers: headers}), + returnValue: Future<_i2.Response>.value(_FakeResponse_1())) + as _i4.Future<_i2.Response>); + @override + _i4.Future<_i2.Response> get(Uri? url, {Map? headers}) => + (super.noSuchMethod(Invocation.method(#get, [url], {#headers: headers}), + returnValue: Future<_i2.Response>.value(_FakeResponse_1())) + as _i4.Future<_i2.Response>); + @override + _i4.Future<_i2.Response> post(Uri? url, + {Map? headers, + Object? body, + _i5.Encoding? encoding}) => + (super.noSuchMethod( + Invocation.method(#post, [url], + {#headers: headers, #body: body, #encoding: encoding}), + returnValue: Future<_i2.Response>.value(_FakeResponse_1())) + as _i4.Future<_i2.Response>); + @override + _i4.Future<_i2.Response> put(Uri? url, + {Map? headers, + Object? body, + _i5.Encoding? encoding}) => + (super.noSuchMethod( + Invocation.method(#put, [url], + {#headers: headers, #body: body, #encoding: encoding}), + returnValue: Future<_i2.Response>.value(_FakeResponse_1())) + as _i4.Future<_i2.Response>); + @override + _i4.Future<_i2.Response> patch(Uri? url, + {Map? headers, + Object? body, + _i5.Encoding? encoding}) => + (super.noSuchMethod( + Invocation.method(#patch, [url], + {#headers: headers, #body: body, #encoding: encoding}), + returnValue: Future<_i2.Response>.value(_FakeResponse_1())) + as _i4.Future<_i2.Response>); + @override + _i4.Future<_i2.Response> delete(Uri? url, + {Map? headers, + Object? body, + _i5.Encoding? encoding}) => + (super.noSuchMethod( + Invocation.method(#delete, [url], + {#headers: headers, #body: body, #encoding: encoding}), + returnValue: Future<_i2.Response>.value(_FakeResponse_1())) + as _i4.Future<_i2.Response>); + @override + _i4.Future read(Uri? url, {Map? headers}) => + (super.noSuchMethod(Invocation.method(#read, [url], {#headers: headers}), + returnValue: Future.value('')) as _i4.Future); + @override + _i4.Future<_i6.Uint8List> readBytes(Uri? url, + {Map? headers}) => (super.noSuchMethod( - Invocation.method(#run, [args], {#ignoreError: ignoreError}), - returnValue: Future.value('')) as _i3.Future); + Invocation.method(#readBytes, [url], {#headers: headers}), + returnValue: Future<_i6.Uint8List>.value(_i6.Uint8List(0))) + as _i4.Future<_i6.Uint8List>); }