master 4649c1e49849 cached
590 files
1.1 MB
265.1k tokens
1 requests
Download .txt
Showing preview only (1,295K chars total). Download the full file or copy to clipboard to get everything.
Repository: nhat-phan/merge-request-integration
Branch: master
Commit: 4649c1e49849
Files: 590
Total size: 1.1 MB

Directory structure:
gitextract_0m11ptlg/

├── .gitignore
├── LICENSE
├── README.md
├── build.gradle.kts
├── contracts/
│   ├── build.gradle.kts
│   ├── settings.gradle.kts
│   └── src/
│       └── main/
│           └── kotlin/
│               └── net/
│                   └── ntworld/
│                       └── mergeRequest/
│                           ├── Approval.kt
│                           ├── Change.kt
│                           ├── Comment.kt
│                           ├── CommentPosition.kt
│                           ├── CommentPositionChangeType.kt
│                           ├── CommentPositionSource.kt
│                           ├── Commit.kt
│                           ├── DateTime.kt
│                           ├── DateTimeSerializer.kt
│                           ├── DiffReference.kt
│                           ├── MergeRequest.kt
│                           ├── MergeRequestInfo.kt
│                           ├── MergeRequestState.kt
│                           ├── Pipeline.kt
│                           ├── PipelineStatus.kt
│                           ├── Project.kt
│                           ├── ProjectVisibility.kt
│                           ├── ProviderData.kt
│                           ├── ProviderInfo.kt
│                           ├── ProviderStatus.kt
│                           ├── User.kt
│                           ├── UserInfo.kt
│                           ├── UserStatus.kt
│                           ├── api/
│                           │   ├── ApiConnection.kt
│                           │   ├── ApiCredentials.kt
│                           │   ├── ApiOptions.kt
│                           │   ├── ApiProvider.kt
│                           │   ├── Cache.kt
│                           │   ├── CacheNotFoundException.kt
│                           │   ├── CommentApi.kt
│                           │   ├── CommitApi.kt
│                           │   ├── DraftCommentStorage.kt
│                           │   ├── MergeRequestApi.kt
│                           │   ├── MergeRequestOrdering.kt
│                           │   ├── ProjectApi.kt
│                           │   └── UserApi.kt
│                           ├── command/
│                           │   ├── ApproveMergeRequestCommand.kt
│                           │   ├── DeleteCommentCommand.kt
│                           │   ├── ResolveCommentCommand.kt
│                           │   ├── UnapproveMergeRequestCommand.kt
│                           │   └── UnresolveCommentCommand.kt
│                           ├── query/
│                           │   ├── FindApprovalQuery.kt
│                           │   ├── FindApprovalQueryResult.kt
│                           │   ├── FindMergeRequestQuery.kt
│                           │   ├── FindMergeRequestQueryResult.kt
│                           │   ├── GetCommentsQuery.kt
│                           │   ├── GetCommentsQueryResult.kt
│                           │   ├── GetCommitsQuery.kt
│                           │   ├── GetCommitsQueryResult.kt
│                           │   ├── GetMergeRequestFilter.kt
│                           │   ├── GetMergeRequestsQuery.kt
│                           │   ├── GetMergeRequestsQueryResult.kt
│                           │   ├── GetPipelinesQuery.kt
│                           │   ├── GetPipelinesQueryResult.kt
│                           │   ├── GetProjectMembersQuery.kt
│                           │   ├── GetProjectMembersQueryResult.kt
│                           │   └── QueryBase.kt
│                           ├── request/
│                           │   ├── CreateCommentRequest.kt
│                           │   ├── PublishAllCommentsRequest.kt
│                           │   ├── PublishCommentsRequest.kt
│                           │   ├── ReplyCommentRequest.kt
│                           │   └── UpdateCommentRequest.kt
│                           └── response/
│                               ├── CreateCommentResponse.kt
│                               ├── PublishAllCommentsResponse.kt
│                               ├── PublishCommentsResponse.kt
│                               ├── ReplyCommentResponse.kt
│                               └── UpdateCommentResponse.kt
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── merge-request-integration/
│   ├── build.gradle.kts
│   ├── settings.gradle.kts
│   └── src/
│       ├── main/
│       │   └── kotlin/
│       │       └── net/
│       │           └── ntworld/
│       │               └── mergeRequestIntegration/
│       │                   ├── DefaultProviderStorage.kt
│       │                   ├── MergeRequestIntegrationInfrastructure.kt
│       │                   ├── ProviderStorage.kt
│       │                   ├── _const.kt
│       │                   ├── commandHandler/
│       │                   │   ├── ApproveMergeRequestCommandHandler.kt
│       │                   │   ├── DeleteCommentCommandHandler.kt
│       │                   │   ├── ResolveCommentCommandHandler.kt
│       │                   │   ├── UnapproveMergeRequestCommandHandler.kt
│       │                   │   └── UnresolveCommentCommandHandler.kt
│       │                   ├── exception/
│       │                   │   ├── InvalidCacheKeyException.kt
│       │                   │   ├── InvalidTTLException.kt
│       │                   │   └── ProviderNotFoundException.kt
│       │                   ├── internal/
│       │                   │   ├── ApiOptionsImpl.kt
│       │                   │   ├── ApprovalImpl.kt
│       │                   │   ├── ChangeImpl.kt
│       │                   │   ├── CommentImpl.kt
│       │                   │   ├── CommentPositionImpl.kt
│       │                   │   ├── CommitImpl.kt
│       │                   │   ├── DiffReferenceImpl.kt
│       │                   │   ├── MergeRequestImpl.kt
│       │                   │   ├── MergeRequestInfoImpl.kt
│       │                   │   ├── MergeRequestSearchResultImpl.kt
│       │                   │   ├── PipelineImpl.kt
│       │                   │   ├── ProjectImpl.kt
│       │                   │   ├── ProviderDataImpl.kt
│       │                   │   ├── UserImpl.kt
│       │                   │   └── UserInfoImpl.kt
│       │                   ├── provider/
│       │                   │   ├── DraftCommentApi.kt
│       │                   │   ├── FuelClient.kt
│       │                   │   ├── MemoryCache.kt
│       │                   │   ├── MemoryDraftCommentStorage.kt
│       │                   │   ├── MergeRequestApiDecorator.kt
│       │                   │   ├── ProviderException.kt
│       │                   │   ├── Transformer.kt
│       │                   │   ├── github/
│       │                   │   │   ├── Github.kt
│       │                   │   │   ├── GithubApiProvider.kt
│       │                   │   │   ├── GithubClient.kt
│       │                   │   │   ├── GithubFailedRequestError.kt
│       │                   │   │   ├── GithubFuelClient.kt
│       │                   │   │   ├── GithubMergeRequestApi.kt
│       │                   │   │   ├── GithubProjectApi.kt
│       │                   │   │   ├── GithubRequest.kt
│       │                   │   │   ├── GithubUserApi.kt
│       │                   │   │   ├── GithubUtil.kt
│       │                   │   │   ├── _const.kt
│       │                   │   │   ├── model/
│       │                   │   │   │   ├── PullRequestSearchItem.kt
│       │                   │   │   │   └── SearchPullRequestResult.kt
│       │                   │   │   ├── request/
│       │                   │   │   │   ├── GithubFindCurrentUserRequest.kt
│       │                   │   │   │   ├── GithubFindRepositoryRequest.kt
│       │                   │   │   │   ├── GithubSearchPRsRequest.kt
│       │                   │   │   │   └── GithubSearchRepositoriesRequest.kt
│       │                   │   │   ├── requestHandler/
│       │                   │   │   │   ├── GithubFindCurrentUserRequestHandler.kt
│       │                   │   │   │   ├── GithubFindRepositoryRequestHandler.kt
│       │                   │   │   │   ├── GithubSearchPRsRequestHandler.kt
│       │                   │   │   │   └── GithubSearchRepositoriesRequestHandler.kt
│       │                   │   │   ├── response/
│       │                   │   │   │   ├── GithubFindRepositoryResponse.kt
│       │                   │   │   │   ├── GithubFindUserResponse.kt
│       │                   │   │   │   ├── GithubSearchPRsResponse.kt
│       │                   │   │   │   └── GithubSearchRepositoriesResponse.kt
│       │                   │   │   ├── transformer/
│       │                   │   │   │   ├── GithubRepositoryTransformer.kt
│       │                   │   │   │   ├── GithubSearchPullRequestItemTransformer.kt
│       │                   │   │   │   └── GithubUserTransformer.kt
│       │                   │   │   └── vo/
│       │                   │   │       ├── GithubMergeRequestId.kt
│       │                   │   │       ├── GithubProjectId.kt
│       │                   │   │       └── GithubUserId.kt
│       │                   │   └── gitlab/
│       │                   │       ├── Gitlab.kt
│       │                   │       ├── GitlabApiProvider.kt
│       │                   │       ├── GitlabClient.kt
│       │                   │       ├── GitlabCommentApi.kt
│       │                   │       ├── GitlabCommitApi.kt
│       │                   │       ├── GitlabCredentials.kt
│       │                   │       ├── GitlabFailedRequestError.kt
│       │                   │       ├── GitlabFailedRequestException.kt
│       │                   │       ├── GitlabFuelClient.kt
│       │                   │       ├── GitlabMergeRequestApi.kt
│       │                   │       ├── GitlabMergeRequestApiCache.kt
│       │                   │       ├── GitlabProjectApi.kt
│       │                   │       ├── GitlabRequest.kt
│       │                   │       ├── GitlabUserApi.kt
│       │                   │       ├── GitlabUtil.kt
│       │                   │       ├── _const.kt
│       │                   │       ├── command/
│       │                   │       │   ├── GitlabApproveMRCommand.kt
│       │                   │       │   ├── GitlabCreateDiffNoteCommand.kt
│       │                   │       │   ├── GitlabDeleteNoteCommand.kt
│       │                   │       │   ├── GitlabResolveNoteCommand.kt
│       │                   │       │   ├── GitlabUnapproveMRCommand.kt
│       │                   │       │   └── GitlabUpdateDiffNoteCommand.kt
│       │                   │       ├── commandHandler/
│       │                   │       │   ├── GitlabApproveMRCommandHandler.kt
│       │                   │       │   ├── GitlabCreateDiffNoteCommandHandler.kt
│       │                   │       │   ├── GitlabDeleteNoteCommandHandler.kt
│       │                   │       │   ├── GitlabResolveNoteCommandHandler.kt
│       │                   │       │   ├── GitlabUnapproveMRCommandHandler.kt
│       │                   │       │   └── GitlabUpdateDiffNoteCommandHandler.kt
│       │                   │       ├── model/
│       │                   │       │   ├── ApprovalModel.kt
│       │                   │       │   ├── ApproverModel.kt
│       │                   │       │   ├── GetCommentsPayload.kt
│       │                   │       │   ├── GraphqlRequest.kt
│       │                   │       │   ├── PipelineModel.kt
│       │                   │       │   ├── ReplyCommentPayload.kt
│       │                   │       │   └── UserInfoModel.kt
│       │                   │       ├── request/
│       │                   │       │   ├── GitlabCreateNoteRequest.kt
│       │                   │       │   ├── GitlabFindCurrentUserRequest.kt
│       │                   │       │   ├── GitlabFindMRApprovalRequest.kt
│       │                   │       │   ├── GitlabFindMRRequest.kt
│       │                   │       │   ├── GitlabFindProjectRequest.kt
│       │                   │       │   ├── GitlabFindUserRequest.kt
│       │                   │       │   ├── GitlabGetCommitChangesRequest.kt
│       │                   │       │   ├── GitlabGetMRChangesRequest.kt
│       │                   │       │   ├── GitlabGetMRCommentsRequest.kt
│       │                   │       │   ├── GitlabGetMRCommitsRequest.kt
│       │                   │       │   ├── GitlabGetMRDiscussionsRequest.kt
│       │                   │       │   ├── GitlabGetMRPipelinesRequest.kt
│       │                   │       │   ├── GitlabGetProjectMembersRequest.kt
│       │                   │       │   ├── GitlabReplyNoteRequest.kt
│       │                   │       │   ├── GitlabSearchMRsRequest.kt
│       │                   │       │   └── GitlabSearchProjectsRequest.kt
│       │                   │       ├── requestHandler/
│       │                   │       │   ├── GitlabCreateNoteRequestHandler.kt
│       │                   │       │   ├── GitlabFindCurrentUserRequestHandler.kt
│       │                   │       │   ├── GitlabFindMRApprovalRequestHandler.kt
│       │                   │       │   ├── GitlabFindMRRequestHandler.kt
│       │                   │       │   ├── GitlabFindProjectRequestHandler.kt
│       │                   │       │   ├── GitlabFindUserRequestHandler.kt
│       │                   │       │   ├── GitlabGetCommitChangesRequestHandler.kt
│       │                   │       │   ├── GitlabGetMRChangesRequestHandler.kt
│       │                   │       │   ├── GitlabGetMRCommentsRequestHandler.kt
│       │                   │       │   ├── GitlabGetMRCommitsRequestHandler.kt
│       │                   │       │   ├── GitlabGetMRDiscussionsRequestHandler.kt
│       │                   │       │   ├── GitlabGetMRPipelinesRequestHandler.kt
│       │                   │       │   ├── GitlabGetProjectMembersRequestHandler.kt
│       │                   │       │   ├── GitlabReplyNoteRequestHandler.kt
│       │                   │       │   ├── GitlabSearchMRsRequestHandler.kt
│       │                   │       │   └── GitlabSearchProjectsRequestHandler.kt
│       │                   │       ├── response/
│       │                   │       │   ├── GitlabCreateNoteResponse.kt
│       │                   │       │   ├── GitlabFindMRApprovalResponse.kt
│       │                   │       │   ├── GitlabFindMRResponse.kt
│       │                   │       │   ├── GitlabFindProjectResponse.kt
│       │                   │       │   ├── GitlabFindUserResponse.kt
│       │                   │       │   ├── GitlabGetCommitChangesResponse.kt
│       │                   │       │   ├── GitlabGetMRChangesResponse.kt
│       │                   │       │   ├── GitlabGetMRCommentsResponse.kt
│       │                   │       │   ├── GitlabGetMRCommitsResponse.kt
│       │                   │       │   ├── GitlabGetMRDiscussionsResponse.kt
│       │                   │       │   ├── GitlabGetMRPipelinesResponse.kt
│       │                   │       │   ├── GitlabGetProjectMembersResponse.kt
│       │                   │       │   ├── GitlabReplyNoteResponse.kt
│       │                   │       │   ├── GitlabSearchMRsResponse.kt
│       │                   │       │   └── GitlabSearchProjectsResponse.kt
│       │                   │       └── transformer/
│       │                   │           ├── GitlabApprovalTransformer.kt
│       │                   │           ├── GitlabCommentTransformer.kt
│       │                   │           ├── GitlabCommitTransformer.kt
│       │                   │           ├── GitlabDiffRefTransformer.kt
│       │                   │           ├── GitlabDiffTransformer.kt
│       │                   │           ├── GitlabDiscussionTransformer.kt
│       │                   │           ├── GitlabMRSimpleTransformer.kt
│       │                   │           ├── GitlabMRTransformer.kt
│       │                   │           ├── GitlabMemberTransformer.kt
│       │                   │           ├── GitlabPipelineTransformer.kt
│       │                   │           ├── GitlabProjectTransformer.kt
│       │                   │           ├── GitlabUserInfoTransformer.kt
│       │                   │           └── GitlabUserTransformer.kt
│       │                   ├── queryHandler/
│       │                   │   ├── FindApprovalQueryHandler.kt
│       │                   │   ├── FindMergeRequestQueryHandler.kt
│       │                   │   ├── GetCommentsQueryHandler.kt
│       │                   │   ├── GetCommitsQueryHandler.kt
│       │                   │   ├── GetMergeRequestsQueryHandler.kt
│       │                   │   ├── GetPipelinesQueryHandler.kt
│       │                   │   └── GetProjectMembersQueryHandler.kt
│       │                   ├── requestHandler/
│       │                   │   ├── CreateCommentRequestHandler.kt
│       │                   │   ├── PublishAllCommentsRequestHandler.kt
│       │                   │   ├── PublishCommentsRequestHandler.kt
│       │                   │   ├── ReplyCommentRequestHandler.kt
│       │                   │   └── UpdateCommentRequestHandler.kt
│       │                   ├── update/
│       │                   │   ├── UpdateManager.kt
│       │                   │   └── UpdateMetadata.kt
│       │                   └── util/
│       │                       ├── DateTimeUtil.kt
│       │                       └── SavedFiltersUtil.kt
│       └── test/
│           ├── kotlin/
│           │   └── net/
│           │       └── ntworld/
│           │           └── mergeRequestIntegration/
│           │               └── provider/
│           │                   └── MemoryCacheTests.kt
│           └── resources/
│               └── update-metadata.json
├── merge-request-integration-ce/
│   ├── build.gradle.kts
│   ├── doc/
│   │   ├── description.html
│   │   ├── release-notes.2019.3.0.html
│   │   ├── release-notes.2019.3.1.html
│   │   ├── release-notes.2019.3.2.html
│   │   ├── release-notes.2019.3.3.html
│   │   ├── release-notes.2019.3.4.html
│   │   ├── release-notes.2019.3.5.html
│   │   ├── release-notes.2020.1.0.html
│   │   ├── release-notes.2020.1.1.html
│   │   ├── release-notes.2020.1.2.html
│   │   ├── release-notes.2020.1.3.html
│   │   ├── release-notes.2020.1.4.html
│   │   ├── release-notes.2020.1.5.html
│   │   ├── release-notes.2020.2.0.html
│   │   └── release-notes.2020.3.0.html
│   ├── settings.gradle.kts
│   └── src/
│       └── main/
│           ├── kotlin/
│           │   └── net/
│           │       └── ntworld/
│           │           └── mergeRequestIntegrationIdeCE/
│           │               ├── CommunityApplicationServiceProvider.kt
│           │               ├── CommunityProjectServiceProvider.kt
│           │               ├── Configuration.kt
│           │               ├── DiffExtension.kt
│           │               ├── DiffViewAddCommentAction.kt
│           │               ├── DiffViewToggleCommentsAction.kt
│           │               ├── GithubConnectionsConfigurable.kt
│           │               ├── GitlabConnectionsConfigurable.kt
│           │               ├── MainToolWindowFactory.kt
│           │               └── SingleMRToolWindowFactory.kt
│           └── resources/
│               └── META-INF/
│                   └── plugin.xml
├── merge-request-integration-core/
│   ├── build.gradle.kts
│   ├── settings.gradle.kts
│   └── src/
│       ├── main/
│       │   ├── kotlin/
│       │   │   └── net/
│       │   │       └── ntworld/
│       │   │           └── mergeRequestIntegrationIde/
│       │   │               ├── AbstractModel.kt
│       │   │               ├── AbstractPresenter.kt
│       │   │               ├── AbstractSimpleModel.kt
│       │   │               ├── AbstractSimplePresenter.kt
│       │   │               ├── AbstractSimpleView.kt
│       │   │               ├── AbstractView.kt
│       │   │               ├── Component.kt
│       │   │               ├── ComponentFactory.kt
│       │   │               ├── DataChangedSource.kt
│       │   │               ├── DefaultComponentFactory.kt
│       │   │               ├── IdeInfrastructure.kt
│       │   │               ├── Model.kt
│       │   │               ├── Presenter.kt
│       │   │               ├── SimpleModel.kt
│       │   │               ├── SimplePresenter.kt
│       │   │               ├── SimpleView.kt
│       │   │               ├── View.kt
│       │   │               ├── _const.kt
│       │   │               ├── compatibility/
│       │   │               │   ├── IntellijIdeApi.kt
│       │   │               │   ├── Version193Adapter.kt
│       │   │               │   ├── Version201Adapter.kt
│       │   │               │   └── Version203Adapter.kt
│       │   │               ├── component/
│       │   │               │   ├── Icons.kt
│       │   │               │   ├── PaginationToolbar.kt
│       │   │               │   ├── PaginationToolbarImpl.kt
│       │   │               │   ├── comment/
│       │   │               │   │   ├── CommentComponent.kt
│       │   │               │   │   ├── CommentComponentFactory.kt
│       │   │               │   │   ├── CommentComponentFactoryImpl.kt
│       │   │               │   │   ├── CommentComponentImpl.kt
│       │   │               │   │   ├── CommentEvent.kt
│       │   │               │   │   ├── CommentEventPropagator.kt
│       │   │               │   │   ├── EditorComponent.kt
│       │   │               │   │   ├── EditorComponentImpl.kt
│       │   │               │   │   ├── GroupComponent.kt
│       │   │               │   │   ├── GroupComponentImpl.kt
│       │   │               │   │   └── Options.kt
│       │   │               │   ├── dialog/
│       │   │               │   │   ├── LegalWarningDialog.form
│       │   │               │   │   └── LegalWarningDialog.kt
│       │   │               │   ├── gutter/
│       │   │               │   │   ├── GutterActionType.kt
│       │   │               │   │   ├── GutterIconRenderer.kt
│       │   │               │   │   ├── GutterIconRendererActionListener.kt
│       │   │               │   │   ├── GutterIconRendererFactory.kt
│       │   │               │   │   ├── GutterIconRendererImpl.kt
│       │   │               │   │   ├── GutterPosition.kt
│       │   │               │   │   └── GutterState.kt
│       │   │               │   └── thread/
│       │   │               │       ├── ThreadFactory.kt
│       │   │               │       ├── ThreadModel.kt
│       │   │               │       ├── ThreadModelImpl.kt
│       │   │               │       ├── ThreadPresenter.kt
│       │   │               │       ├── ThreadPresenterImpl.kt
│       │   │               │       ├── ThreadView.kt
│       │   │               │       └── ThreadViewImpl.kt
│       │   │               ├── configuration/
│       │   │               │   ├── README.md
│       │   │               │   └── vos/
│       │   │               │       └── GitRemotePathInfo.kt
│       │   │               ├── diff/
│       │   │               │   ├── AbstractDiffView.kt
│       │   │               │   ├── CommentPoint.kt
│       │   │               │   ├── DiffExtensionBase.kt
│       │   │               │   ├── DiffFactory.kt
│       │   │               │   ├── DiffModel.kt
│       │   │               │   ├── DiffModelImpl.kt
│       │   │               │   ├── DiffPresenter.kt
│       │   │               │   ├── DiffPresenterImpl.kt
│       │   │               │   ├── DiffView.kt
│       │   │               │   ├── DiffViewAddCommentActionBase.kt
│       │   │               │   ├── DiffViewToggleCommentsActionBase.kt
│       │   │               │   ├── SimpleOneSideDiffView.kt
│       │   │               │   ├── TwoSideTextDiffView.kt
│       │   │               │   └── UnifiedDiffView.kt
│       │   │               ├── exception/
│       │   │               │   └── InvalidConnectionException.kt
│       │   │               ├── infrastructure/
│       │   │               │   ├── AbstractApplicationServiceProvider.kt
│       │   │               │   ├── AbstractProjectServiceProvider.kt
│       │   │               │   ├── ApplicationServiceProvider.kt
│       │   │               │   ├── ProjectServiceProvider.kt
│       │   │               │   ├── ProviderSettings.kt
│       │   │               │   ├── ReviewContext.kt
│       │   │               │   ├── ReviewContextManager.kt
│       │   │               │   ├── internal/
│       │   │               │   │   ├── ApiCredentialsImpl.kt
│       │   │               │   │   ├── DiffPreviewProviderImpl.kt
│       │   │               │   │   ├── DiffRequestProcessorImpl.kt
│       │   │               │   │   ├── ProviderSettingsImpl.kt
│       │   │               │   │   ├── ReviewContextImpl.kt
│       │   │               │   │   ├── ReviewContextManagerImpl.kt
│       │   │               │   │   └── ServiceBase.kt
│       │   │               │   ├── notifier/
│       │   │               │   │   ├── DiffNotifier.kt
│       │   │               │   │   ├── MergeRequestDataNotifier.kt
│       │   │               │   │   ├── ProjectNotifier.kt
│       │   │               │   │   ├── ProjectNotifierAdapter.kt
│       │   │               │   │   ├── ReworkEditorNotifier.kt
│       │   │               │   │   ├── ReworkWatcherNotifier.kt
│       │   │               │   │   ├── SingleMRToolWindowNotifier.kt
│       │   │               │   │   └── provider/
│       │   │               │   │       └── MergeRequestDataProvider.kt
│       │   │               │   ├── service/
│       │   │               │   │   ├── FiltersStorageService.kt
│       │   │               │   │   ├── RepositoryFileService.kt
│       │   │               │   │   ├── internal/
│       │   │               │   │   │   └── FiltersStorageServiceImpl.kt
│       │   │               │   │   └── repositoryFile/
│       │   │               │   │       ├── CachedRepositoryFile.kt
│       │   │               │   │       ├── LocalRepositoryFileService.kt
│       │   │               │   │       └── RepositoryFileDecorator.kt
│       │   │               │   └── setting/
│       │   │               │       ├── ApplicationSettings.kt
│       │   │               │       ├── ApplicationSettingsImpl.kt
│       │   │               │       ├── ApplicationSettingsManager.kt
│       │   │               │       ├── ApplicationSettingsManagerImpl.kt
│       │   │               │       └── option/
│       │   │               │           ├── BooleanOption.kt
│       │   │               │           ├── CheckoutTargetBranchOption.kt
│       │   │               │           ├── DisplayCommentsInDiffViewOption.kt
│       │   │               │           ├── DisplayMergeRequestStateOption.kt
│       │   │               │           ├── DisplayUpVotesAndDownVotesOption.kt
│       │   │               │           ├── EnableRequestCacheOption.kt
│       │   │               │           ├── EnableReworkProcessOption.kt
│       │   │               │           ├── MaxDiffChangesOpenedAutomaticallyOption.kt
│       │   │               │           ├── SaveMRFilterStateOption.kt
│       │   │               │           ├── SettingOption.kt
│       │   │               │           └── ShowAddCommentIconsInDiffViewGutterOption.kt
│       │   │               ├── mergeRequest/
│       │   │               │   ├── comments/
│       │   │               │   │   ├── CommentsTabFactory.kt
│       │   │               │   │   ├── CommentsTabModel.kt
│       │   │               │   │   ├── CommentsTabModelImpl.kt
│       │   │               │   │   ├── CommentsTabPresenter.kt
│       │   │               │   │   ├── CommentsTabPresenterImpl.kt
│       │   │               │   │   ├── CommentsTabView.kt
│       │   │               │   │   ├── CommentsTabViewImpl.kt
│       │   │               │   │   └── tree/
│       │   │               │   │       ├── CommentTreeFactory.kt
│       │   │               │   │       ├── CommentTreeModel.kt
│       │   │               │   │       ├── CommentTreeModelImpl.kt
│       │   │               │   │       ├── CommentTreePresenter.kt
│       │   │               │   │       ├── CommentTreePresenterImpl.kt
│       │   │               │   │       ├── CommentTreeView.kt
│       │   │               │   │       ├── CommentTreeViewImpl.kt
│       │   │               │   │       ├── CommentTreeViewToolbar.kt
│       │   │               │   │       └── node/
│       │   │               │   │           ├── AbstractNode.kt
│       │   │               │   │           ├── CommentNode.kt
│       │   │               │   │           ├── FileLineNode.kt
│       │   │               │   │           ├── FileNode.kt
│       │   │               │   │           ├── GeneralCommentsNode.kt
│       │   │               │   │           ├── Node.kt
│       │   │               │   │           ├── NodeDescriptorService.kt
│       │   │               │   │           ├── NodeDescriptorServiceImpl.kt
│       │   │               │   │           ├── NodeFactory.kt
│       │   │               │   │           ├── NodeSyncManager.kt
│       │   │               │   │           ├── NodeSyncManagerImpl.kt
│       │   │               │   │           ├── RootNode.kt
│       │   │               │   │           ├── RootNodeBuilder.kt
│       │   │               │   │           ├── SyncedTree.kt
│       │   │               │   │           ├── ThreadNode.kt
│       │   │               │   │           └── _fn.kt
│       │   │               │   └── fn.kt
│       │   │               ├── rework/
│       │   │               │   ├── BranchWatcher.kt
│       │   │               │   ├── ReworkEditorController.kt
│       │   │               │   ├── ReworkEditorManager.kt
│       │   │               │   ├── ReworkManager.kt
│       │   │               │   ├── ReworkWatcher.kt
│       │   │               │   └── internal/
│       │   │               │       ├── BranchWatcherImpl.kt
│       │   │               │       ├── ReworkEditorControllerImpl.kt
│       │   │               │       ├── ReworkEditorManagerImpl.kt
│       │   │               │       ├── ReworkGeneralCommentsView.kt
│       │   │               │       ├── ReworkManagerImpl.kt
│       │   │               │       └── ReworkWatcherImpl.kt
│       │   │               ├── task/
│       │   │               │   ├── FetchProjectMembersTask.kt
│       │   │               │   ├── FindApprovalTask.kt
│       │   │               │   ├── FindMergeRequestTask.kt
│       │   │               │   ├── GetAvailableUpdatesTask.kt
│       │   │               │   ├── GetCommentsTask.kt
│       │   │               │   ├── GetCommitsTask.kt
│       │   │               │   ├── GetPipelinesTask.kt
│       │   │               │   ├── RegisterProviderTask.kt
│       │   │               │   ├── RepositoryFetchAllRemotesTask.kt
│       │   │               │   └── SearchMergeRequestTask.kt
│       │   │               ├── toolWindow/
│       │   │               │   ├── CommentsToolWindowTab.kt
│       │   │               │   ├── FilesToolWindowTab.kt
│       │   │               │   ├── ReworkToolWindowTab.kt
│       │   │               │   ├── SingleMRToolWindowFactoryBase.kt
│       │   │               │   ├── SingleMRToolWindowManager.kt
│       │   │               │   └── internal/
│       │   │               │       ├── CommentsToolWindowTabImpl.kt
│       │   │               │       └── FilesToolWindowTabImpl.kt
│       │   │               ├── ui/
│       │   │               │   ├── Component.kt
│       │   │               │   ├── MainToolWindowFactoryBase.kt
│       │   │               │   ├── README.md
│       │   │               │   ├── configuration/
│       │   │               │   │   ├── AbstractConnectionsConfigurable.kt
│       │   │               │   │   ├── ConfigurationBase.kt
│       │   │               │   │   ├── ConnectionUI.kt
│       │   │               │   │   ├── GithubConnection.form
│       │   │               │   │   ├── GithubConnection.kt
│       │   │               │   │   ├── GithubConnectionsConfigurableBase.kt
│       │   │               │   │   ├── GithubProjectFinder.kt
│       │   │               │   │   ├── GitlabConnection.form
│       │   │               │   │   ├── GitlabConnection.kt
│       │   │               │   │   ├── GitlabConnectionsConfigurableBase.kt
│       │   │               │   │   ├── GitlabProjectFinder.kt
│       │   │               │   │   ├── ProjectFinderUI.kt
│       │   │               │   │   ├── SettingsConfiguration.form
│       │   │               │   │   ├── SettingsConfiguration.kt
│       │   │               │   │   └── SettingsUI.kt
│       │   │               │   ├── mergeRequest/
│       │   │               │   │   ├── AbstractMergeRequestCollection.kt
│       │   │               │   │   ├── MergeRequestCollection.kt
│       │   │               │   │   ├── MergeRequestCollectionEventListener.kt
│       │   │               │   │   ├── MergeRequestCollectionFilter.kt
│       │   │               │   │   ├── MergeRequestCollectionFilterEventListener.kt
│       │   │               │   │   ├── MergeRequestCollectionFilterUI.kt
│       │   │               │   │   ├── MergeRequestCollectionTree.kt
│       │   │               │   │   ├── MergeRequestCollectionTreeNode.kt
│       │   │               │   │   ├── MergeRequestCollectionUI.kt
│       │   │               │   │   ├── MergeRequestDetails.kt
│       │   │               │   │   ├── MergeRequestDetailsToolbar.kt
│       │   │               │   │   ├── MergeRequestDetailsToolbarUI.kt
│       │   │               │   │   ├── MergeRequestDetailsUI.kt
│       │   │               │   │   └── tab/
│       │   │               │   │       ├── MergeRequestCommitsTab.kt
│       │   │               │   │       ├── MergeRequestCommitsTabUI.kt
│       │   │               │   │       ├── MergeRequestDescriptionTab.kt
│       │   │               │   │       ├── MergeRequestDescriptionTabUI.kt
│       │   │               │   │       ├── MergeRequestInfoTab.kt
│       │   │               │   │       ├── MergeRequestInfoTabUI.kt
│       │   │               │   │       └── commit/
│       │   │               │   │           ├── CommitChanges.kt
│       │   │               │   │           ├── CommitChangesUI.kt
│       │   │               │   │           ├── CommitCollection.kt
│       │   │               │   │           ├── CommitCollectionUI.kt
│       │   │               │   │           └── CommitSelectUtil.kt
│       │   │               │   ├── panel/
│       │   │               │   │   ├── ApprovalPanel.form
│       │   │               │   │   ├── ApprovalPanel.kt
│       │   │               │   │   ├── CommitItemPanel.form
│       │   │               │   │   ├── CommitItemPanel.kt
│       │   │               │   │   ├── MergeRequestFilterPropertiesPanel.form
│       │   │               │   │   ├── MergeRequestFilterPropertiesPanel.kt
│       │   │               │   │   ├── MergeRequestInfoPanel.form
│       │   │               │   │   ├── MergeRequestInfoPanel.kt
│       │   │               │   │   ├── MergeRequestItemPanel.form
│       │   │               │   │   ├── MergeRequestItemPanel.kt
│       │   │               │   │   ├── ProjectPanel.form
│       │   │               │   │   ├── ProjectPanel.kt
│       │   │               │   │   ├── ProviderInformationPanel.form
│       │   │               │   │   ├── ProviderInformationPanel.kt
│       │   │               │   │   ├── ProviderItemPanel.form
│       │   │               │   │   ├── ProviderItemPanel.kt
│       │   │               │   │   ├── UserInfoItemPanel.form
│       │   │               │   │   └── UserInfoItemPanel.kt
│       │   │               │   ├── provider/
│       │   │               │   │   ├── ProviderCollection.kt
│       │   │               │   │   ├── ProviderCollectionList.kt
│       │   │               │   │   ├── ProviderCollectionListEventListener.kt
│       │   │               │   │   ├── ProviderCollectionListUI.kt
│       │   │               │   │   ├── ProviderCollectionToolbar.kt
│       │   │               │   │   ├── ProviderCollectionToolbarEventListener.kt
│       │   │               │   │   ├── ProviderCollectionToolbarUI.kt
│       │   │               │   │   ├── ProviderDetails.kt
│       │   │               │   │   ├── ProviderDetailsMRList.kt
│       │   │               │   │   └── ProviderDetailsUI.kt
│       │   │               │   ├── service/
│       │   │               │   │   ├── CheckoutService.kt
│       │   │               │   │   ├── CodeReviewService.kt
│       │   │               │   │   ├── DisplayChangesService.kt
│       │   │               │   │   ├── EditorStateService.kt
│       │   │               │   │   └── FetchService.kt
│       │   │               │   ├── toolWindowTab/
│       │   │               │   │   ├── HomeToolWindowTab.kt
│       │   │               │   │   ├── MergeRequestToolWindowTab.kt
│       │   │               │   │   └── UpdateInfoTab.kt
│       │   │               │   └── util/
│       │   │               │       ├── CustomSimpleToolWindowPanel.kt
│       │   │               │       ├── ImageUtil.kt
│       │   │               │       ├── Tabs.kt
│       │   │               │       ├── TabsUI.kt
│       │   │               │       ├── ToolbarUtil.kt
│       │   │               │       └── fn.kt
│       │   │               ├── util/
│       │   │               │   ├── CommentUtil.kt
│       │   │               │   ├── FileTypeUtil.kt
│       │   │               │   ├── HtmlHelper.kt
│       │   │               │   ├── RepositoryUtil.kt
│       │   │               │   └── TextChoiceUtil.kt
│       │   │               └── watcher/
│       │   │                   ├── Watcher.kt
│       │   │                   ├── WatcherManager.kt
│       │   │                   └── WatcherManagerImpl.kt
│       │   └── resources/
│       │       └── templates/
│       │           ├── mr.comment.html
│       │           ├── mr.description.html
│       │           └── update.html
│       └── test/
│           └── kotlin/
│               └── net/
│                   └── ntworld/
│                       └── mergeRequestIntegrationIde/
│                           ├── configuration/
│                           │   └── vos/
│                           │       └── GitRemotePathInfoTest.kt
│                           ├── infrastructure/
│                           │   └── DummyProjectServiceProvider.kt
│                           ├── internal/
│                           │   └── CodeReviewServiceImplTest.kt
│                           ├── mergeRequest/
│                           │   └── comments/
│                           │       └── tree/
│                           │           └── node/
│                           │               └── NodeSyncManagerImplTest.kt
│                           └── watcher/
│                               └── WatcherManagerImplTest.kt
├── merge-request-integration-ee/
│   ├── LICENSE
│   ├── build.gradle.kts
│   ├── doc/
│   │   ├── description.html
│   │   ├── release-notes.2019.3.1.html
│   │   ├── release-notes.2019.3.2.html
│   │   ├── release-notes.2019.3.3.html
│   │   ├── release-notes.2019.3.4.html
│   │   ├── release-notes.2019.3.5.html
│   │   ├── release-notes.2020.1.0.html
│   │   ├── release-notes.2020.1.1.html
│   │   ├── release-notes.2020.1.2.html
│   │   ├── release-notes.2020.1.3.html
│   │   ├── release-notes.2020.1.4.html
│   │   ├── release-notes.2020.1.5.html
│   │   ├── release-notes.2020.2.0.html
│   │   └── release-notes.2020.3.0.html
│   ├── settings.gradle.kts
│   └── src/
│       └── main/
│           ├── kotlin/
│           │   └── net/
│           │       └── ntworld/
│           │           └── mergeRequestIntegrationIdeEE/
│           │               ├── CheckLicense.kt
│           │               ├── Configuration.kt
│           │               ├── DiffExtension.kt
│           │               ├── DiffViewAddCommentAction.kt
│           │               ├── DiffViewToggleCommentsAction.kt
│           │               ├── EnterpriseApplicationServiceProvider.kt
│           │               ├── EnterpriseProjectServiceProvider.kt
│           │               ├── GithubConnectionsConfigurable.kt
│           │               ├── GitlabConnectionsConfigurable.kt
│           │               ├── MainToolWindowFactory.kt
│           │               └── SingleMRToolWindowFactory.kt
│           └── resources/
│               └── META-INF/
│                   └── plugin.xml
└── settings.gradle.kts

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build

out

.kotlintest
.idea
*.iml
*.ipr
*.iws

internal-test/src


================================================
FILE: LICENSE
================================================
Merge Request Integration Community Edition (CE) (c) 2019-present Nhat Phan <nhat.phan@ntworld.net> (https://github.com/nhat-phan)

Merge Request Integration Community Edition (CE) is licensed under a
Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

You should have received a copy of the license along with this
work. If not, see <http://creativecommons.org/licenses/by-nc-nd/4.0/>.

Attribution-NonCommercial-NoDerivatives 4.0 International

=======================================================================

Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.

Using Creative Commons Public Licenses

Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.

     Considerations for licensors: Our public licenses are
     intended for use by those authorized to give the public
     permission to use material in ways otherwise restricted by
     copyright and certain other rights. Our licenses are
     irrevocable. Licensors should read and understand the terms
     and conditions of the license they choose before applying it.
     Licensors should also secure all rights necessary before
     applying our licenses so that the public can reuse the
     material as expected. Licensors should clearly mark any
     material not subject to the license. This includes other CC-
     licensed material, or material used under an exception or
     limitation to copyright. More considerations for licensors:
	wiki.creativecommons.org/Considerations_for_licensors

     Considerations for the public: By using one of our public
     licenses, a licensor grants the public permission to use the
     licensed material under specified terms and conditions. If
     the licensor's permission is not necessary for any reason--for
     example, because of any applicable exception or limitation to
     copyright--then that use is not regulated by the license. Our
     licenses grant only permissions under copyright and certain
     other rights that a licensor has authority to grant. Use of
     the licensed material may still be restricted for other
     reasons, including because others have copyright or other
     rights in the material. A licensor may make special requests,
     such as asking that all changes be marked or described.
     Although not required by our licenses, you are encouraged to
     respect those requests where reasonable. More_considerations
     for the public:
	wiki.creativecommons.org/Considerations_for_licensees

=======================================================================

Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
International Public License

By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-NonCommercial-NoDerivatives 4.0 International Public
License ("Public License"). To the extent this Public License may be
interpreted as a contract, You are granted the Licensed Rights in
consideration of Your acceptance of these terms and conditions, and the
Licensor grants You such rights in consideration of benefits the
Licensor receives from making the Licensed Material available under
these terms and conditions.


Section 1 -- Definitions.

  a. Adapted Material means material subject to Copyright and Similar
     Rights that is derived from or based upon the Licensed Material
     and in which the Licensed Material is translated, altered,
     arranged, transformed, or otherwise modified in a manner requiring
     permission under the Copyright and Similar Rights held by the
     Licensor. For purposes of this Public License, where the Licensed
     Material is a musical work, performance, or sound recording,
     Adapted Material is always produced where the Licensed Material is
     synched in timed relation with a moving image.

  b. Copyright and Similar Rights means copyright and/or similar rights
     closely related to copyright including, without limitation,
     performance, broadcast, sound recording, and Sui Generis Database
     Rights, without regard to how the rights are labeled or
     categorized. For purposes of this Public License, the rights
     specified in Section 2(b)(1)-(2) are not Copyright and Similar
     Rights.

  c. Effective Technological Measures means those measures that, in the
     absence of proper authority, may not be circumvented under laws
     fulfilling obligations under Article 11 of the WIPO Copyright
     Treaty adopted on December 20, 1996, and/or similar international
     agreements.

  d. Exceptions and Limitations means fair use, fair dealing, and/or
     any other exception or limitation to Copyright and Similar Rights
     that applies to Your use of the Licensed Material.

  e. Licensed Material means the artistic or literary work, database,
     or other material to which the Licensor applied this Public
     License.

  f. Licensed Rights means the rights granted to You subject to the
     terms and conditions of this Public License, which are limited to
     all Copyright and Similar Rights that apply to Your use of the
     Licensed Material and that the Licensor has authority to license.

  g. Licensor means the individual(s) or entity(ies) granting rights
     under this Public License.

  h. NonCommercial means not primarily intended for or directed towards
     commercial advantage or monetary compensation. For purposes of
     this Public License, the exchange of the Licensed Material for
     other material subject to Copyright and Similar Rights by digital
     file-sharing or similar means is NonCommercial provided there is
     no payment of monetary compensation in connection with the
     exchange.

  i. Share means to provide material to the public by any means or
     process that requires permission under the Licensed Rights, such
     as reproduction, public display, public performance, distribution,
     dissemination, communication, or importation, and to make material
     available to the public including in ways that members of the
     public may access the material from a place and at a time
     individually chosen by them.

  j. Sui Generis Database Rights means rights other than copyright
     resulting from Directive 96/9/EC of the European Parliament and of
     the Council of 11 March 1996 on the legal protection of databases,
     as amended and/or succeeded, as well as other essentially
     equivalent rights anywhere in the world.

  k. You means the individual or entity exercising the Licensed Rights
     under this Public License. Your has a corresponding meaning.


Section 2 -- Scope.

  a. License grant.

       1. Subject to the terms and conditions of this Public License,
          the Licensor hereby grants You a worldwide, royalty-free,
          non-sublicensable, non-exclusive, irrevocable license to
          exercise the Licensed Rights in the Licensed Material to:

            a. reproduce and Share the Licensed Material, in whole or
               in part, for NonCommercial purposes only; and

            b. produce and reproduce, but not Share, Adapted Material
               for NonCommercial purposes only.

       2. Exceptions and Limitations. For the avoidance of doubt, where
          Exceptions and Limitations apply to Your use, this Public
          License does not apply, and You do not need to comply with
          its terms and conditions.

       3. Term. The term of this Public License is specified in Section
          6(a).

       4. Media and formats; technical modifications allowed. The
          Licensor authorizes You to exercise the Licensed Rights in
          all media and formats whether now known or hereafter created,
          and to make technical modifications necessary to do so. The
          Licensor waives and/or agrees not to assert any right or
          authority to forbid You from making technical modifications
          necessary to exercise the Licensed Rights, including
          technical modifications necessary to circumvent Effective
          Technological Measures. For purposes of this Public License,
          simply making modifications authorized by this Section 2(a)
          (4) never produces Adapted Material.

       5. Downstream recipients.

            a. Offer from the Licensor -- Licensed Material. Every
               recipient of the Licensed Material automatically
               receives an offer from the Licensor to exercise the
               Licensed Rights under the terms and conditions of this
               Public License.

            b. No downstream restrictions. You may not offer or impose
               any additional or different terms or conditions on, or
               apply any Effective Technological Measures to, the
               Licensed Material if doing so restricts exercise of the
               Licensed Rights by any recipient of the Licensed
               Material.

       6. No endorsement. Nothing in this Public License constitutes or
          may be construed as permission to assert or imply that You
          are, or that Your use of the Licensed Material is, connected
          with, or sponsored, endorsed, or granted official status by,
          the Licensor or others designated to receive attribution as
          provided in Section 3(a)(1)(A)(i).

  b. Other rights.

       1. Moral rights, such as the right of integrity, are not
          licensed under this Public License, nor are publicity,
          privacy, and/or other similar personality rights; however, to
          the extent possible, the Licensor waives and/or agrees not to
          assert any such rights held by the Licensor to the limited
          extent necessary to allow You to exercise the Licensed
          Rights, but not otherwise.

       2. Patent and trademark rights are not licensed under this
          Public License.

       3. To the extent possible, the Licensor waives any right to
          collect royalties from You for the exercise of the Licensed
          Rights, whether directly or through a collecting society
          under any voluntary or waivable statutory or compulsory
          licensing scheme. In all other cases the Licensor expressly
          reserves any right to collect such royalties, including when
          the Licensed Material is used other than for NonCommercial
          purposes.


Section 3 -- License Conditions.

Your exercise of the Licensed Rights is expressly made subject to the
following conditions.

  a. Attribution.

       1. If You Share the Licensed Material, You must:

            a. retain the following if it is supplied by the Licensor
               with the Licensed Material:

                 i. identification of the creator(s) of the Licensed
                    Material and any others designated to receive
                    attribution, in any reasonable manner requested by
                    the Licensor (including by pseudonym if
                    designated);

                ii. a copyright notice;

               iii. a notice that refers to this Public License;

                iv. a notice that refers to the disclaimer of
                    warranties;

                 v. a URI or hyperlink to the Licensed Material to the
                    extent reasonably practicable;

            b. indicate if You modified the Licensed Material and
               retain an indication of any previous modifications; and

            c. indicate the Licensed Material is licensed under this
               Public License, and include the text of, or the URI or
               hyperlink to, this Public License.

          For the avoidance of doubt, You do not have permission under
          this Public License to Share Adapted Material.

       2. You may satisfy the conditions in Section 3(a)(1) in any
          reasonable manner based on the medium, means, and context in
          which You Share the Licensed Material. For example, it may be
          reasonable to satisfy the conditions by providing a URI or
          hyperlink to a resource that includes the required
          information.

       3. If requested by the Licensor, You must remove any of the
          information required by Section 3(a)(1)(A) to the extent
          reasonably practicable.


Section 4 -- Sui Generis Database Rights.

Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:

  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
     to extract, reuse, reproduce, and Share all or a substantial
     portion of the contents of the database for NonCommercial purposes
     only and provided You do not Share Adapted Material;

  b. if You include all or a substantial portion of the database
     contents in a database in which You have Sui Generis Database
     Rights, then the database in which You have Sui Generis Database
     Rights (but not its individual contents) is Adapted Material; and

  c. You must comply with the conditions in Section 3(a) if You Share
     all or a substantial portion of the contents of the database.

For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.


Section 5 -- Disclaimer of Warranties and Limitation of Liability.

  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.

  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.

  c. The disclaimer of warranties and limitation of liability provided
     above shall be interpreted in a manner that, to the extent
     possible, most closely approximates an absolute disclaimer and
     waiver of all liability.


Section 6 -- Term and Termination.

  a. This Public License applies for the term of the Copyright and
     Similar Rights licensed here. However, if You fail to comply with
     this Public License, then Your rights under this Public License
     terminate automatically.

  b. Where Your right to use the Licensed Material has terminated under
     Section 6(a), it reinstates:

       1. automatically as of the date the violation is cured, provided
          it is cured within 30 days of Your discovery of the
          violation; or

       2. upon express reinstatement by the Licensor.

     For the avoidance of doubt, this Section 6(b) does not affect any
     right the Licensor may have to seek remedies for Your violations
     of this Public License.

  c. For the avoidance of doubt, the Licensor may also offer the
     Licensed Material under separate terms or conditions or stop
     distributing the Licensed Material at any time; however, doing so
     will not terminate this Public License.

  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
     License.


Section 7 -- Other Terms and Conditions.

  a. The Licensor shall not be bound by any additional or different
     terms or conditions communicated by You unless expressly agreed.

  b. Any arrangements, understandings, or agreements regarding the
     Licensed Material not stated herein are separate from and
     independent of the terms and conditions of this Public License.


Section 8 -- Interpretation.

  a. For the avoidance of doubt, this Public License does not, and
     shall not be interpreted to, reduce, limit, restrict, or impose
     conditions on any use of the Licensed Material that could lawfully
     be made without permission under this Public License.

  b. To the extent possible, if any provision of this Public License is
     deemed unenforceable, it shall be automatically reformed to the
     minimum extent necessary to make it enforceable. If the provision
     cannot be reformed, it shall be severed from this Public License
     without affecting the enforceability of the remaining terms and
     conditions.

  c. No term or condition of this Public License will be waived and no
     failure to comply consented to unless expressly agreed to by the
     Licensor.

  d. Nothing in this Public License constitutes or may be interpreted
     as a limitation upon, or waiver of, any privileges and immunities
     that apply to the Licensor or You, including from the legal
     processes of any jurisdiction or authority.

=======================================================================

Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.

Creative Commons may be contacted at creativecommons.org.



================================================
FILE: README.md
================================================
## Merge Request Integration

Merge Request Integration is a plugin which helps you to do Code Review right in your IDE.

<img src="https://raw.githubusercontent.com/nhat-phan/merge-request-integration/master/images/v2019.3.2.gif" alt="Merge Request Integration CE" style="width: 100%" />


What you can do:

- Filter Merge Requests which are assigned to you, waiting for your approval, etc
- Check pipeline status and approval status.
- Select and review 1 or all commits
- Do code review, navigate code with Diff View right in your IDE.
- Add, reply, resolve or delete comments
- Approve/revoke your approval
- More and more features will be coming soon :)

Currently the plugin supports GitLab only (gitlab cloud and self-hosted).

You can download the plugin on intellij plugins repository: 
[Community Edition](https://plugins.jetbrains.com/plugin/13607-merge-request-integration-ce--code-review-for-gitlab/),
[Enterprise Edition](https://plugins.jetbrains.com/plugin/13615-merge-request-integration-ee--code-review-for-gitlab/)

### How to set up Gitlab connection

#### Create Gitlab Personal Access Tokens

To get the your Personal Access Token please follow these steps:

<img src="https://raw.githubusercontent.com/nhat-phan/merge-request-integration/master/images/gitlab-1.png" />

Step 1: On top right corner > Settings

<img src="https://raw.githubusercontent.com/nhat-phan/merge-request-integration/master/images/gitlab-2.png" />

Step 2: Click to Access Tokens menu

<img src="https://raw.githubusercontent.com/nhat-phan/merge-request-integration/master/images/gitlab-3.png" />

Step 3: Create a Personal Access Tokens with api scope

#### Config connection in your IDE Settings

After creating the Personal Access Tokens:

- Go to your IDE preferences (macOS: `⌘,` Windows: `Ctrl+Alt+S`)
- Merge Request Integration > Gitlab
- Fill data, then save. *Tip: you can use Starred/Membership/Own option to search your project quicker.* 
- Click refresh button of Merge Request Integration CE window if you don't see the connection.

If your project has more than 1 repository, just setup multiple connections.

### License

The plugin is an open source released under Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International
license.

It's totally free if you are using it for public repositories.

For private repositories, this plugin is a trial. How long is the trial period? Equal to WINRAR's trial period 🙈

[Community Edition (CE)](https://plugins.jetbrains.com/plugin/13607-merge-request-integration-ce--code-review-for-gitlab/) 
is exactly the same as 
[Enterprise Edition (EE)](https://plugins.jetbrains.com/plugin/13615-merge-request-integration-ee--code-review-for-gitlab/). 
You don't need to hack or find a cracked version.
Cracking software invites virus to your computer.

### About me

My name is Nhat, I'm a software developer at [Personio](https://personio.com) 
(yes, 
[we are hiring](https://www.personio.com/about-personio/jobs/) 
all around the world, relocation to Munich is of course possible).

### Sponsor

If you love this plugin, please support me by:

- Buy an [Enterprise Edition](https://plugins.jetbrains.com/plugin/13615-merge-request-integration-ee--code-review-for-gitlab/), only 1$/month
- Buy me a beer via [Paypal](https://paypal.me/phanhoangnhat) or [Patreon](https://www.patreon.com/nhat/creators).

Thanks in advance!

### Attribution

- Icons by [Font Awesome](https://fontawesome.com/) are licensed under 
[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)


================================================
FILE: build.gradle.kts
================================================
plugins {
    // "org.jetbrains.kotlin.jvm"
    kotlin("jvm") version "1.3.50" apply false

    // "org.jetbrains.kotlin.kapt"
    kotlin("kapt") version "1.3.50" apply false

    // "kotlinx-serialization"
    id("kotlinx-serialization") version "1.3.50" apply false

    id("org.jetbrains.intellij") version "0.4.12" apply false
}

subprojects {
    if (name == "contracts") {
        apply(plugin = "org.jetbrains.kotlin.jvm")
        apply(plugin = "org.jetbrains.kotlin.kapt")
        apply(plugin = "kotlinx-serialization")
    }

    if (name == "merge-request-integration") {
        apply(plugin = "org.jetbrains.kotlin.jvm")
        apply(plugin = "org.jetbrains.kotlin.kapt")
        apply(plugin = "kotlinx-serialization")
    }

    if (name == "merge-request-integration-core") {
        apply(plugin = "org.jetbrains.intellij")
        apply(plugin = "org.jetbrains.kotlin.jvm")
    }

    if (name == "merge-request-integration-ce") {
        apply(plugin = "org.jetbrains.intellij")
        apply(plugin = "org.jetbrains.kotlin.jvm")
    }

    if (name == "merge-request-integration-ee") {
        apply(plugin = "org.jetbrains.intellij")
        apply(plugin = "org.jetbrains.kotlin.jvm")
    }
}

================================================
FILE: contracts/build.gradle.kts
================================================
val artifactGroup: String by project
val artifactVersion: String by project
val jvmTarget: String by project
val foundationVersion: String by project
val foundationProcessorVersion: String by project
val jodaTimeVersion: String by project
val kotlinxSerializationRuntimeVersion: String by project

group = artifactGroup
version = artifactVersion

repositories {
    jcenter()
    mavenCentral()
    mavenLocal()
    maven("https://jitpack.io")
}

dependencies {
    implementation(kotlin("stdlib"))
    implementation("com.github.nhat-phan.foundation:foundation-jvm:$foundationVersion")
    implementation("joda-time:joda-time:$jodaTimeVersion")
    compile("org.jetbrains.kotlinx:kotlinx-serialization-runtime:$kotlinxSerializationRuntimeVersion")

    kapt("com.github.nhat-phan.foundation:foundation-processor:$foundationProcessorVersion")
    kaptTest("com.github.nhat-phan.foundation:foundation-processor:$foundationProcessorVersion")

    testImplementation(kotlin("test"))
    testImplementation(kotlin("test-junit"))
}

kapt {
    arguments {
        arg("foundation.processor.mode", "contractOnly")
        arg("foundation.processor.settingsClass", "net.ntworld.mergeRequestIntegration.ContractData")
    }
}

tasks {
    named<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>("compileKotlin") {
        kotlinOptions {
            jvmTarget = jvmTarget
        }
    }
}


================================================
FILE: contracts/settings.gradle.kts
================================================
rootProject.name = "contracts"


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/Approval.kt
================================================
package net.ntworld.mergeRequest

interface Approval {
    val approved: Boolean

    val approvalsRequired: Int

    val approvalsLeft: Int

    val approvers: List<UserInfo>

    val suggestedApprovers: List<UserInfo>

    val approvedBy: List<UserInfo>

    val hasApproved: Boolean

    val canApprove: Boolean

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/Change.kt
================================================
package net.ntworld.mergeRequest

interface Change {
    val oldPath: String
    val newPath: String
    val aMode: String
    val bMode: String
    val newFile: Boolean
    val renamedFile: Boolean
    val deletedFile: Boolean

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/Comment.kt
================================================
package net.ntworld.mergeRequest

interface Comment {
    val id: String

    val parentId: String

    val replyId: String

    val body: String

    val author: UserInfo

    val position: CommentPosition?

    val createdAt: DateTime

    val updatedAt: DateTime

    val resolvable: Boolean

    val resolved: Boolean

    val resolvedBy: UserInfo?

    val isDraft: Boolean

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/CommentPosition.kt
================================================
package net.ntworld.mergeRequest

interface CommentPosition {
    val baseHash: String

    val startHash: String

    val headHash: String

    val oldPath: String?

    val newPath: String?

    val oldLine: Int?

    val newLine: Int?

    val source: CommentPositionSource

    val changeType: CommentPositionChangeType

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/CommentPositionChangeType.kt
================================================
package net.ntworld.mergeRequest

enum class CommentPositionChangeType{
    UNKNOWN,
    INSERTED,
    DELETED,
    MODIFIED
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/CommentPositionSource.kt
================================================
package net.ntworld.mergeRequest

enum class CommentPositionSource {
    UNKNOWN,
    SERVER,
    SINGLE_SIDE,
    SIDE_BY_SIDE_LEFT,
    SIDE_BY_SIDE_RIGHT,
    UNIFIED
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/Commit.kt
================================================
package net.ntworld.mergeRequest

interface Commit {
    val id: String

    val message: String

    val authorName: String

    val authorEmail: String

    val createdAt: String

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/DateTime.kt
================================================
package net.ntworld.mergeRequest

typealias DateTime = String

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/DateTimeSerializer.kt
================================================
@file:UseSerializers(DateTimeSerializer::class)

package net.ntworld.mergeRequest

import kotlinx.serialization.*
import kotlinx.serialization.internal.SerialClassDescImpl
import org.joda.time.DateTime

/**
 * TODO: Cannot use until foundation support @SerializerFor(...)
 */
@Serializer(forClass = DateTime::class)
object DateTimeSerializer : KSerializer<DateTime> {

    override val descriptor: SerialDescriptor = SerialClassDescImpl("DateTime")

    override fun deserialize(decoder: Decoder): DateTime {
        return DateTime(decoder.decodeString())
    }

    override fun serialize(encoder: Encoder, obj: DateTime) {
        encoder.encodeString(obj.toString())
    }

}




================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/DiffReference.kt
================================================
package net.ntworld.mergeRequest

interface DiffReference {
    val baseHash: String

    val headHash: String

    val startHash: String

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/MergeRequest.kt
================================================
package net.ntworld.mergeRequest

interface MergeRequest: MergeRequestInfo {
    val assignee: UserInfo?

    val author: UserInfo

    val diffReference: DiffReference?

    val sourceBranch: String

    val targetBranch: String

    val upVotes: Int

    val downVotes: Int

    val commentsCount: Int

    val isWorkInProgress: Boolean

    val canMerged: Boolean

    val mergedBy: UserInfo?

    val closedBy: UserInfo?

    val mergedAt: DateTime?

    val closedAt: DateTime?

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/MergeRequestInfo.kt
================================================
package net.ntworld.mergeRequest

interface MergeRequestInfo {
    val id: String

    val provider: String

    val projectId: String

    val title: String

    val description: String

    val url: String

    val state: MergeRequestState

    val createdAt: DateTime

    val updatedAt: DateTime

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/MergeRequestState.kt
================================================
package net.ntworld.mergeRequest

enum class MergeRequestState {
    ALL,
    OPENED,
    CLOSED,
    MERGED
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/Pipeline.kt
================================================
package net.ntworld.mergeRequest

interface Pipeline {
    val id: String

    val hash: String

    val ref: String

    val status: PipelineStatus

    val url: String

    val createdAt: DateTime?

    val updatedAt: DateTime?

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/PipelineStatus.kt
================================================
package net.ntworld.mergeRequest

enum class PipelineStatus {
    FAILED,
    RUNNING,
    PARTIAL_FAILED,
    SUCCESS,
    UNKNOWN,
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/Project.kt
================================================
package net.ntworld.mergeRequest

interface Project {
    val id: String

    val provider: ProviderInfo

    val name: String

    val path: String

    val url: String

    val avatarUrl: String

    val visibility: ProjectVisibility

    val repositoryHttpUrl: String

    val repositorySshUrl: String

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/ProjectVisibility.kt
================================================
package net.ntworld.mergeRequest

enum class ProjectVisibility {
    PUBLIC,
    PRIVATE
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/ProviderData.kt
================================================
package net.ntworld.mergeRequest

import net.ntworld.mergeRequest.api.ApiCredentials

interface ProviderData {
    val id: String

    val key: String

    val name: String

    val info: ProviderInfo

    val credentials: ApiCredentials

    val project: Project

    val currentUser: User

    val repository: String

    val errorMessage: String?

    val status: ProviderStatus

    val hasApprovalFeature: Boolean
        get() = false

    val hasAssigneeFeature: Boolean
        get() = false

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/ProviderInfo.kt
================================================
package net.ntworld.mergeRequest

interface ProviderInfo {
    val id: String

    val name: String

    val iconPath: String

    val icon2xPath: String

    val icon3xPath: String

    val icon4xPath: String

    fun createCommentUrl(mergeRequestUrl: String, comment: Comment): String

    fun formatMergeRequestId(mergeRequestId: String): String

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/ProviderStatus.kt
================================================
package net.ntworld.mergeRequest

enum class ProviderStatus {
    ACTIVE,
    ERROR
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/User.kt
================================================
package net.ntworld.mergeRequest

interface User : UserInfo {
    val email: String

    val createdAt: DateTime

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/UserInfo.kt
================================================
package net.ntworld.mergeRequest

interface UserInfo {
    val id: String

    val name: String

    val username: String

    val avatarUrl: String?

    val url: String

    val status: UserStatus

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/UserStatus.kt
================================================
package net.ntworld.mergeRequest

enum class UserStatus {
    ACTIVE,
    INACTIVE
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/ApiConnection.kt
================================================
package net.ntworld.mergeRequest.api

interface ApiConnection {
    val url: String

    val ignoreSSLCertificateErrors: Boolean

    val login: String

    val token: String
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/ApiCredentials.kt
================================================
package net.ntworld.mergeRequest.api

interface ApiCredentials : ApiConnection {
    val projectId: String

    val version: String

    val info: String
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/ApiOptions.kt
================================================
package net.ntworld.mergeRequest.api

interface ApiOptions {
    val enableRequestCache: Boolean
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/ApiProvider.kt
================================================
package net.ntworld.mergeRequest.api

import net.ntworld.mergeRequest.ProviderInfo
import net.ntworld.mergeRequest.User
import net.ntworld.mergeRequest.UserInfo

interface ApiProvider {
    val info: ProviderInfo

    val credentials: ApiCredentials

    val cache: Cache

    val user: UserApi

    val mergeRequest: MergeRequestApi

    val project: ProjectApi

    val comment: CommentApi

    val commit: CommitApi

    fun setOptions(options: ApiOptions)

    fun initialize(currentUser: UserInfo)
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/Cache.kt
================================================
package net.ntworld.mergeRequest.api

import org.joda.time.DateTime

interface Cache {
    val defaultTTL: Int

    fun <T> get(key: String): T

    fun has(key: String): Boolean

    fun remove(key: String)

    fun put(key: String, value: Any, ttl: Int)

    fun isExpiredAfter(key: String, datetime: DateTime): Boolean

    fun put(key: String, value: Any) = put(key, value, defaultTTL)

    fun set(key: String, value: Any) = put(key, value, defaultTTL)

    fun set(key: String, value: Any, ttl: Int) = put(key, value, ttl)

    @Suppress("UNCHECKED_CAST")
    fun<T> getOrRun(key: String, run: (() -> T)): T {
        try {
            if (!this.has(key)) {
                return run()
            }
            return this.get(key)
        } catch (exception: CacheNotFoundException) {
            return run()
        }
    }

    fun removeIfExpiredAfter(key: String, datetime: DateTime) {
        if (isExpiredAfter(key, datetime)) {
            remove(key)
        }
    }
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/CacheNotFoundException.kt
================================================
package net.ntworld.mergeRequest.api

class CacheNotFoundException : Throwable()

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/CommentApi.kt
================================================
package net.ntworld.mergeRequest.api

import net.ntworld.mergeRequest.Comment
import net.ntworld.mergeRequest.CommentPosition
import net.ntworld.mergeRequest.Project

interface CommentApi {
    fun getAll(project: Project, mergeRequestId: String): List<Comment>

    fun create(
        project: Project,
        mergeRequestId: String,
        body: String,
        position: CommentPosition?,
        isDraft: Boolean
    ): String?

    fun reply(project: Project, mergeRequestId: String, repliedComment: Comment, body: String): String?

    fun delete(project: Project, mergeRequestId: String, comment: Comment)

    fun resolve(project: Project, mergeRequestId: String, comment: Comment)

    fun unresolve(project: Project, mergeRequestId: String, comment: Comment)

    fun update(project: Project, mergeRequestId: String, comment: Comment, body: String)

    fun hasDraft(project: Project, mergeRequestId: String): Boolean {
        return this.getDraftCount(project, mergeRequestId) > 0
    }

    fun getDraftCount(project: Project, mergeRequestId: String): Int

    fun publishAllDraftComments(project: Project, mergeRequestId: String)

    fun publishDraftComments(project: Project, mergeRequestId: String, commentIds: List<String>)
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/CommitApi.kt
================================================
package net.ntworld.mergeRequest.api

import net.ntworld.mergeRequest.Change

interface CommitApi {

    fun getChanges(projectId: String, commitId: String): List<Change>

}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/DraftCommentStorage.kt
================================================
package net.ntworld.mergeRequest.api

import net.ntworld.mergeRequest.Comment
import net.ntworld.mergeRequest.CommentPosition
import net.ntworld.mergeRequest.Project

interface DraftCommentStorage {
    fun getAll(project: Project, mergeRequestId: String): List<Comment>

    fun findById(project: Project, mergeRequestId: String, commentId: String): Comment?

    fun create(project: Project, mergeRequestId: String, body: String, position: CommentPosition?): String?

    fun update(project: Project, mergeRequestId: String, comment: Comment, body: String)

    fun delete(project: Project, mergeRequestId: String, comment: Comment)
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/MergeRequestApi.kt
================================================
package net.ntworld.mergeRequest.api

import net.ntworld.mergeRequest.*
import net.ntworld.mergeRequest.query.GetMergeRequestFilter

interface MergeRequestApi {
    fun find(projectId: String, mergeRequestId: String): MergeRequest?

    fun approve(projectId: String, mergeRequestId: String, sha: String)

    fun unapprove(projectId: String, mergeRequestId: String)

    fun findApproval(projectId: String, mergeRequestId: String): Approval

    fun getPipelines(projectId: String, mergeRequestId: String): List<Pipeline>

    fun getCommits(projectId: String, mergeRequestId: String): List<Commit>

    fun getChanges(projectId: String, mergeRequestId: String): List<Change>

    fun search(
        projectId: String,
        currentUserId: String,
        filterBy: GetMergeRequestFilter,
        orderBy: MergeRequestOrdering,
        page: Int,
        itemsPerPage: Int
    ): SearchResult

    fun findOrFail(projectId: String, mergeRequestId: String): MergeRequest

    interface SearchResult {
        val data: List<MergeRequestInfo>

        val totalPages: Int

        val totalItems: Int

        val currentPage: Int
    }
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/MergeRequestOrdering.kt
================================================
package net.ntworld.mergeRequest.api

enum class MergeRequestOrdering {
    RECENTLY_UPDATED,
    NEWEST,
    OLDEST
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/ProjectApi.kt
================================================
package net.ntworld.mergeRequest.api

import net.ntworld.mergeRequest.Project
import net.ntworld.mergeRequest.UserInfo

interface ProjectApi {
    fun find(projectId: String): Project?

    fun getMembers(projectId: String): List<UserInfo>

    fun findOrFail(projectId: String): Project {
        val project = find(projectId)
        if (null === project) {
            throw Exception("Project $projectId not found.")
        }
        return project
    }
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/api/UserApi.kt
================================================
package net.ntworld.mergeRequest.api

import net.ntworld.mergeRequest.User

interface UserApi {
    // @Deprecated("Not used, will be removed", ReplaceWith("Nothing"), DeprecationLevel.HIDDEN)
    // fun find(id: String): UserInfo

    fun me(): User
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/command/ApproveMergeRequestCommand.kt
================================================
package net.ntworld.mergeRequest.command

import net.ntworld.foundation.cqrs.Command

interface ApproveMergeRequestCommand : Command {
    val providerId: String

    val mergeRequestId: String

    val sha: String

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/command/DeleteCommentCommand.kt
================================================
package net.ntworld.mergeRequest.command

import net.ntworld.foundation.cqrs.Command
import net.ntworld.mergeRequest.Comment

interface DeleteCommentCommand : Command {
    val providerId: String

    val mergeRequestId: String

    val comment: Comment

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/command/ResolveCommentCommand.kt
================================================
package net.ntworld.mergeRequest.command

import net.ntworld.foundation.cqrs.Command
import net.ntworld.mergeRequest.Comment

interface ResolveCommentCommand : Command {
    val providerId: String

    val mergeRequestId: String

    val comment: Comment

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/command/UnapproveMergeRequestCommand.kt
================================================
package net.ntworld.mergeRequest.command

import net.ntworld.foundation.cqrs.Command

interface UnapproveMergeRequestCommand : Command {
    val providerId: String

    val mergeRequestId: String

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/command/UnresolveCommentCommand.kt
================================================
package net.ntworld.mergeRequest.command

import net.ntworld.foundation.cqrs.Command
import net.ntworld.mergeRequest.Comment

interface UnresolveCommentCommand : Command {
    val providerId: String

    val mergeRequestId: String

    val comment: Comment

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/FindApprovalQuery.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.Query

interface FindApprovalQuery : QueryBase, Query<FindApprovalQueryResult> {
    val mergeRequestId: String

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/FindApprovalQueryResult.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.QueryResult
import net.ntworld.mergeRequest.Approval

interface FindApprovalQueryResult : QueryResult {
    val approval: Approval

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/FindMergeRequestQuery.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.Query

interface FindMergeRequestQuery: QueryBase, Query<FindMergeRequestQueryResult> {
    val mergeRequestId: String

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/FindMergeRequestQueryResult.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.QueryResult
import net.ntworld.mergeRequest.MergeRequest

interface FindMergeRequestQueryResult : QueryResult {
    val mergeRequest: MergeRequest

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetCommentsQuery.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.Query

interface GetCommentsQuery : QueryBase, Query<GetCommentsQueryResult> {
    val mergeRequestId: String

    companion object
}



================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetCommentsQueryResult.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.QueryResult
import net.ntworld.mergeRequest.Comment

interface GetCommentsQueryResult : QueryResult {
    val comments: List<Comment>

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetCommitsQuery.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.Query

interface GetCommitsQuery : QueryBase,
    Query<GetCommitsQueryResult> {
    val mergeRequestId: String

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetCommitsQueryResult.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.QueryResult
import net.ntworld.mergeRequest.Commit

interface GetCommitsQueryResult : QueryResult {
    val commits: List<Commit>

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetMergeRequestFilter.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.mergeRequest.MergeRequestState

interface GetMergeRequestFilter {
    val id: Int?

    val state: MergeRequestState

    val search: String

    val authorId: String

    val assigneeId: String

    val approverIds: List<String>

    val sourceBranch: String

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetMergeRequestsQuery.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.Query
import net.ntworld.mergeRequest.api.MergeRequestOrdering

interface GetMergeRequestsQuery : QueryBase, Query<GetMergeRequestsQueryResult> {
    val filterBy: GetMergeRequestFilter

    val orderBy: MergeRequestOrdering

    val page: Int

    val itemsPerPage: Int

    companion object
}



================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetMergeRequestsQueryResult.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.QueryResult
import net.ntworld.mergeRequest.MergeRequestInfo

interface GetMergeRequestsQueryResult : QueryResult {
    val mergeRequests: List<MergeRequestInfo>

    val totalPages: Int

    val totalItems: Int

    val currentPage: Int

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetPipelinesQuery.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.Query

interface GetPipelinesQuery : QueryBase,
    Query<GetPipelinesQueryResult> {
    val mergeRequestId: String

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetPipelinesQueryResult.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.QueryResult
import net.ntworld.mergeRequest.Pipeline

interface GetPipelinesQueryResult : QueryResult {
    val pipelines: List<Pipeline>

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetProjectMembersQuery.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.Query

interface GetProjectMembersQuery : QueryBase, Query<GetProjectMembersQueryResult> {
    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetProjectMembersQueryResult.kt
================================================
package net.ntworld.mergeRequest.query

import net.ntworld.foundation.cqrs.QueryResult
import net.ntworld.mergeRequest.UserInfo

interface GetProjectMembersQueryResult : QueryResult {
    val members: List<UserInfo>

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/query/QueryBase.kt
================================================
package net.ntworld.mergeRequest.query

interface QueryBase {
    val providerId: String
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/request/CreateCommentRequest.kt
================================================
package net.ntworld.mergeRequest.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.CommentPosition
import net.ntworld.mergeRequest.response.CreateCommentResponse

interface CreateCommentRequest : Request<CreateCommentResponse> {
    val providerId: String

    val mergeRequestId: String

    val body: String

    val position: CommentPosition?

    val isDraft: Boolean

    companion object
}


================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/request/PublishAllCommentsRequest.kt
================================================
package net.ntworld.mergeRequest.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.response.PublishAllCommentsResponse

interface PublishAllCommentsRequest : Request<PublishAllCommentsResponse> {
    val providerId: String

    val mergeRequestId: String

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/request/PublishCommentsRequest.kt
================================================
package net.ntworld.mergeRequest.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.response.PublishCommentsResponse

interface PublishCommentsRequest : Request<PublishCommentsResponse> {
    val providerId: String

    val mergeRequestId: String

    val draftCommentIds: List<String>

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/request/ReplyCommentRequest.kt
================================================
package net.ntworld.mergeRequest.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.Comment
import net.ntworld.mergeRequest.response.ReplyCommentResponse

interface ReplyCommentRequest : Request<ReplyCommentResponse> {
    val providerId: String

    val mergeRequestId: String

    val repliedComment: Comment

    val body: String

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/request/UpdateCommentRequest.kt
================================================
package net.ntworld.mergeRequest.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.Comment
import net.ntworld.mergeRequest.response.UpdateCommentResponse

interface UpdateCommentRequest : Request<UpdateCommentResponse> {
    val providerId: String

    val mergeRequestId: String

    val comment: Comment

    val body: String

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/response/CreateCommentResponse.kt
================================================
package net.ntworld.mergeRequest.response

import net.ntworld.foundation.Response

interface CreateCommentResponse : Response {
    val createdCommentId: String?

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/response/PublishAllCommentsResponse.kt
================================================
package net.ntworld.mergeRequest.response

import net.ntworld.foundation.Response

interface PublishAllCommentsResponse : Response {
    val success: Boolean

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/response/PublishCommentsResponse.kt
================================================
package net.ntworld.mergeRequest.response

import net.ntworld.foundation.Response

interface PublishCommentsResponse : Response {
    val success: Boolean

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/response/ReplyCommentResponse.kt
================================================
package net.ntworld.mergeRequest.response

import net.ntworld.foundation.Response

interface ReplyCommentResponse : Response {
    val createdCommentId: String?

    companion object
}

================================================
FILE: contracts/src/main/kotlin/net/ntworld/mergeRequest/response/UpdateCommentResponse.kt
================================================
package net.ntworld.mergeRequest.response

import net.ntworld.foundation.Response

interface UpdateCommentResponse : Response {
    val commentId: String?

    companion object
}

================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Thu Oct 10 17:57:14 CEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.3.1-all.zip


================================================
FILE: gradle.properties
================================================
artifactGroup=net.ntworld.nhat-phan.merge-request-integration

# Please also change version number in
# net.ntworld.mergeRequestIntegration.update.UpdateManager
eapRelease=false
artifactVersion=2020.3.0
targetIDEVersion=2020.3.x
communityEditionVersion=2020.3.0
enterpriseEditionVersion=2020.3.0

# intellijVersion=LATEST-EAP-SNAPSHOT
intellijVersion=2020.3
intellijSinceBuild=203
intellijUntilBuild=203.*

jvmTarget=1.8

foundationVersion=0.6
foundationProcessorVersion=0.6.1

kotlinxSerializationRuntimeVersion=0.11.1
javaFakerVersion=1.0.0

jodaTimeVersion=2.10.5

fuelVersion=2.2.1

gitlab4jVersion=4.14.5
githubApiVersion=1.101

prettyTimeVersion=4.0.1.Final

commonmarkVersion=0.13.0

mockkVersion=1.9.3


================================================
FILE: gradlew
================================================
#!/usr/bin/env sh

#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
    echo "$*"
}

die () {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=$((i+1))
    done
    case $i in
        (0) set -- ;;
        (1) set -- "$args0" ;;
        (2) set -- "$args0" "$args1" ;;
        (3) set -- "$args0" "$args1" "$args2" ;;
        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=$(save "$@")

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
  cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"


================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem      http://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem

@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: merge-request-integration/build.gradle.kts
================================================
val artifactGroup: String by project
val artifactVersion: String by project
val jvmTarget: String by project
val foundationVersion: String by project
val foundationProcessorVersion: String by project
val kotlinxSerializationRuntimeVersion: String by project
val javaFakerVersion: String by project
val jodaTimeVersion: String by project
val fuelVersion: String by project
val gitlab4jVersion: String by project
val githubApiVersion: String by project
val prettyTimeVersion: String by project
val commonmarkVersion: String by project

group = artifactGroup
version = artifactVersion

repositories {
    jcenter()
    mavenCentral()
    mavenLocal()
    maven("https://jitpack.io")
}

dependencies {
    implementation(kotlin("stdlib"))
    implementation("com.github.nhat-phan.foundation:foundation-jvm:$foundationVersion")
    implementation(project(":contracts"))
    implementation("joda-time:joda-time:$jodaTimeVersion")
    implementation("com.github.kittinunf.fuel:fuel:$fuelVersion")
    implementation("org.gitlab4j:gitlab4j-api:$gitlab4jVersion")
    implementation("org.kohsuke:github-api:$githubApiVersion")
    implementation("org.ocpsoft.prettytime:prettytime:$prettyTimeVersion")

    compile("com.atlassian.commonmark:commonmark:$commonmarkVersion")
    compile("org.jetbrains.kotlinx:kotlinx-serialization-runtime:$kotlinxSerializationRuntimeVersion")
    compile("com.github.javafaker:javafaker:$javaFakerVersion")

    kapt("com.github.nhat-phan.foundation:foundation-processor:$foundationProcessorVersion")
    kaptTest("com.github.nhat-phan.foundation:foundation-processor:$foundationProcessorVersion")

    testImplementation(kotlin("test"))
    testImplementation(kotlin("test-junit"))
    testImplementation("io.mockk:mockk:1.9")
    testImplementation("io.kotlintest:kotlintest-runner-junit5:3.3.0")
}

tasks.withType<Test> {
    useJUnitPlatform()
}

kapt {
    arguments {
        arg("foundation.processor.globalNamespace", "net.ntworld.mergeRequestIntegration")
    }
}

tasks {
    named<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>("compileKotlin") {
        kotlinOptions {
            jvmTarget = jvmTarget
        }
    }
}


================================================
FILE: merge-request-integration/settings.gradle.kts
================================================
rootProject.name = "merge-request-integration"


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/DefaultProviderStorage.kt
================================================
package net.ntworld.mergeRequestIntegration

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.*
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequest.api.ApiOptions
import net.ntworld.mergeRequest.api.ApiProvider
import net.ntworld.mergeRequestIntegration.exception.ProviderNotFoundException
import net.ntworld.mergeRequestIntegration.internal.ProjectImpl
import net.ntworld.mergeRequestIntegration.internal.ProviderDataImpl
import net.ntworld.mergeRequestIntegration.internal.UserImpl
import net.ntworld.mergeRequestIntegration.provider.MemoryCache
import net.ntworld.mergeRequestIntegration.provider.gitlab.Gitlab
import net.ntworld.mergeRequestIntegration.provider.gitlab.GitlabApiProvider
import java.util.*

class DefaultProviderStorage : ProviderStorage {
    private val data = Collections.synchronizedMap(mutableMapOf<String, ProviderData>())
    private val api = Collections.synchronizedMap(mutableMapOf<String, ApiProvider>())

    override val registeredProviders
        get() = data.values.toList()

    override fun updateApiOptions(options: ApiOptions) {
        api.forEach { it.value.setOptions(options) }
    }

    override fun register(
        infrastructure: Infrastructure,
        id: String,
        key: String,
        name: String,
        info: ProviderInfo,
        credentials: ApiCredentials,
        repository: String
    ): Pair<ProviderData, Throwable?> {
        val api = createApiProvider(infrastructure = infrastructure, id = id, info = info, credentials = credentials)
        var throwable: Throwable? = null
        var message: String? = null
        try {
            val user = api.user.me()
            api.initialize(user)

            val project = api.project.find(credentials.projectId)
            if (null !== project) {
                val providerData = ProviderDataImpl(
                    id = id,
                    key = key,
                    name = name,
                    info = info,
                    credentials = credentials,
                    project = project,
                    currentUser = user,
                    repository = repository,
                    errorMessage = null,
                    status = ProviderStatus.ACTIVE
                )
                data[id] = providerData
                return Pair(providerData, null)
            }
        } catch (exception: Throwable) {
            message = exception.message
            throwable = exception
        }
        val invalid = ProviderDataImpl(
            id = id,
            key = key,
            name = name,
            info = info,
            credentials = credentials,
            project = ProjectImpl("", info, "", "", "", "", ProjectVisibility.PUBLIC, "", ""),
            currentUser = UserImpl("", "[Error]", "<error>", "", "", UserStatus.INACTIVE, "", ""),
            repository = repository,
            errorMessage = message,
            status = ProviderStatus.ERROR
        )
        data[id] = invalid
        return Pair(invalid, throwable)
    }

    override fun clear() {
        data.clear()
        api.clear()
    }

    private fun createApiProvider(
        infrastructure: Infrastructure,
        id: String,
        info: ProviderInfo,
        credentials: ApiCredentials
    ): ApiProvider {
        val created = when (info.id) {
            Gitlab.id -> GitlabApiProvider(
                infrastructure = infrastructure,
                credentials = credentials,
                cache = MemoryCache()
            )
            // Github.id -> GithubApiProvider(
            //     infrastructure = infrastructure,
            //     credentials = credentials,
            //     cache = MemoryCache()
            // )
            else -> throw Exception("Cannot create ApiProvider ${info.id}")
        }
        api[id] = created
        return created
    }

    override fun findOrFail(id: String): Pair<ProviderData, ApiProvider> {
        return Pair(findDataOrFail(id), findProviderOrFail(id))
    }

    private fun findDataOrFail(id: String): ProviderData {
        val item = data[id]
        return if (null !== item) {
            item
        } else {
            throw ProviderNotFoundException()
        }
    }

    private fun findProviderOrFail(id: String): ApiProvider {
        val provider = api[id]
        return if (null !== provider) {
            provider
        } else {
            throw ProviderNotFoundException()
        }
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/MergeRequestIntegrationInfrastructure.kt
================================================
package net.ntworld.mergeRequestIntegration

import net.ntworld.foundation.InfrastructureProvider

class MergeRequestIntegrationInfrastructure(
    private val providerStorage: ProviderStorage
) : InfrastructureProvider() {
    private val included = listOf(
        AutoGeneratedInfrastructureProvider(providerStorage)
    )

    init {
        wire(this.root, this.included)
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/ProviderStorage.kt
================================================
package net.ntworld.mergeRequestIntegration

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.ProviderData
import net.ntworld.mergeRequest.ProviderInfo
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequest.api.ApiOptions
import net.ntworld.mergeRequest.api.ApiProvider

interface ProviderStorage {

    val registeredProviders: List<ProviderData>

    fun updateApiOptions(options: ApiOptions)

    fun register(
        infrastructure: Infrastructure,
        id: String,
        key: String,
        name: String,
        info: ProviderInfo,
        credentials: ApiCredentials,
        repository: String
    ): Pair<ProviderData, Throwable?>

    fun clear()

    fun findOrFail(id: String): Pair<ProviderData, ApiProvider>

}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/_const.kt
================================================
package net.ntworld.mergeRequestIntegration

const val DEFAULT_DATETIME = "1970-01-01T00:00:00Z"

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/commandHandler/ApproveMergeRequestCommandHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.commandHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.cqrs.CommandHandler
import net.ntworld.mergeRequest.command.ApproveMergeRequestCommand
import net.ntworld.mergeRequestIntegration.ProviderStorage

@Handler
class ApproveMergeRequestCommandHandler(
    private val providerStorage: ProviderStorage
) : CommandHandler<ApproveMergeRequestCommand> {
    override fun handle(command: ApproveMergeRequestCommand) {
        val (data, api) = providerStorage.findOrFail(command.providerId)
        api.mergeRequest.approve(
            projectId = data.project.id,
            mergeRequestId = command.mergeRequestId,
            sha = command.sha
        )
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/commandHandler/DeleteCommentCommandHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.commandHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.cqrs.CommandHandler
import net.ntworld.mergeRequest.command.DeleteCommentCommand
import net.ntworld.mergeRequestIntegration.ProviderStorage

@Handler
class DeleteCommentCommandHandler(
    private val providerStorage: ProviderStorage
) : CommandHandler<DeleteCommentCommand> {
    override fun handle(command: DeleteCommentCommand) {
        val (data, api) = providerStorage.findOrFail(command.providerId)
        api.comment.delete(data.project, command.mergeRequestId, command.comment)
    }
}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/commandHandler/ResolveCommentCommandHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.commandHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.cqrs.CommandHandler
import net.ntworld.mergeRequest.command.ResolveCommentCommand
import net.ntworld.mergeRequestIntegration.ProviderStorage

@Handler
class ResolveCommentCommandHandler(
    private val providerStorage: ProviderStorage
) : CommandHandler<ResolveCommentCommand> {
    override fun handle(command: ResolveCommentCommand) {
        val (data, api) = providerStorage.findOrFail(command.providerId)
        api.comment.resolve(data.project, command.mergeRequestId, command.comment)
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/commandHandler/UnapproveMergeRequestCommandHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.commandHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.cqrs.CommandHandler
import net.ntworld.mergeRequest.command.UnapproveMergeRequestCommand
import net.ntworld.mergeRequestIntegration.ProviderStorage

@Handler
class UnapproveMergeRequestCommandHandler(
    private val providerStorage: ProviderStorage
) : CommandHandler<UnapproveMergeRequestCommand> {
    override fun handle(command: UnapproveMergeRequestCommand) {
        val (data, api) = providerStorage.findOrFail(command.providerId)
        api.mergeRequest.unapprove(
            projectId = data.project.id,
            mergeRequestId = command.mergeRequestId
        )
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/commandHandler/UnresolveCommentCommandHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.commandHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.cqrs.CommandHandler
import net.ntworld.mergeRequest.command.UnresolveCommentCommand
import net.ntworld.mergeRequestIntegration.ProviderStorage

@Handler
class UnresolveCommentCommandHandler(
    private val providerStorage: ProviderStorage
) : CommandHandler<UnresolveCommentCommand> {
    override fun handle(command: UnresolveCommentCommand) {
        val (data, api) = providerStorage.findOrFail(command.providerId)
        api.comment.unresolve(data.project, command.mergeRequestId, command.comment)
    }
}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/exception/InvalidCacheKeyException.kt
================================================
package net.ntworld.mergeRequestIntegration.exception

import java.lang.Exception

class InvalidCacheKeyException(message: String): Exception(message)

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/exception/InvalidTTLException.kt
================================================
package net.ntworld.mergeRequestIntegration.exception

class InvalidTTLException: Exception()

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/exception/ProviderNotFoundException.kt
================================================
package net.ntworld.mergeRequestIntegration.exception

import java.lang.Exception

class ProviderNotFoundException: Exception()

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/ApiOptionsImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.api.ApiOptions

data class ApiOptionsImpl(override val enableRequestCache: Boolean) : ApiOptions {
    companion object {
        val DEFAULT = ApiOptionsImpl(
            enableRequestCache = true
        )
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/ApprovalImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.Approval
import net.ntworld.mergeRequest.UserInfo

data class ApprovalImpl(
    override val approved: Boolean,
    override val approvalsRequired: Int,
    override val approvalsLeft: Int,
    override val suggestedApprovers: List<UserInfo>,
    override val approvers: List<UserInfo>,
    override val approvedBy: List<UserInfo>,
    override val hasApproved: Boolean,
    override val canApprove: Boolean
) : Approval


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/ChangeImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.Change

data class ChangeImpl(
    override val oldPath: String,
    override val newPath: String,
    override val aMode: String,
    override val bMode: String,
    override val newFile: Boolean,
    override val renamedFile: Boolean,
    override val deletedFile: Boolean
) : Change

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/CommentImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.Comment
import net.ntworld.mergeRequest.CommentPosition
import net.ntworld.mergeRequest.DateTime
import net.ntworld.mergeRequest.UserInfo

data class CommentImpl(
    override val id: String,
    override val parentId: String,
    override val replyId: String,
    override val body: String,
    override val author: UserInfo,
    override val position: CommentPosition?,
    override val createdAt: DateTime,
    override val updatedAt: DateTime,
    override val resolvable: Boolean,
    override val resolved: Boolean,
    override val resolvedBy: UserInfo?,
    override val isDraft: Boolean
) : Comment {
}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/CommentPositionImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.CommentPosition
import net.ntworld.mergeRequest.CommentPositionChangeType
import net.ntworld.mergeRequest.CommentPositionSource

data class CommentPositionImpl(
    override val baseHash: String,
    override val startHash: String,
    override val headHash: String,
    override val oldPath: String?,
    override val newPath: String?,
    override val oldLine: Int?,
    override val newLine: Int?,
    override val source: CommentPositionSource,
    override val changeType: CommentPositionChangeType
): CommentPosition

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/CommitImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.Commit

data class CommitImpl(
    override val id: String,
    override val message: String,
    override val authorName: String,
    override val authorEmail: String,
    override val createdAt: String
) : Commit

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/DiffReferenceImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.DiffReference

data class DiffReferenceImpl(
    override val baseHash: String,
    override val headHash: String,
    override val startHash: String
): DiffReference

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/MergeRequestImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.*

class MergeRequestImpl(
    override val id: String,
    override val provider: String,
    override val projectId: String,
    override val title: String,
    override val description: String,
    override val url: String,
    override val state: MergeRequestState,
    override val createdAt: DateTime,
    override val updatedAt: DateTime,
    override val assignee: UserInfo?,
    override val author: UserInfo,
    override val diffReference: DiffReference?,
    override val sourceBranch: String,
    override val targetBranch: String,
    override val upVotes: Int,
    override val downVotes: Int,
    override val commentsCount: Int,
    override val isWorkInProgress: Boolean,
    override val canMerged: Boolean,
    override val mergedBy: UserInfo?,
    override val closedBy: UserInfo?,
    override val mergedAt: DateTime?,
    override val closedAt: DateTime?
) : MergeRequest

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/MergeRequestInfoImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.DateTime
import net.ntworld.mergeRequest.MergeRequestInfo
import net.ntworld.mergeRequest.MergeRequestState

data class MergeRequestInfoImpl(
    override val id: String,
    override val provider: String,
    override val projectId: String,
    override val title: String,
    override val description: String,
    override val url: String,
    override val state: MergeRequestState,
    override val createdAt: DateTime,
    override val updatedAt: DateTime
) : MergeRequestInfo

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/MergeRequestSearchResultImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.MergeRequestInfo
import net.ntworld.mergeRequest.api.MergeRequestApi

data class MergeRequestSearchResultImpl(
    override val data: List<MergeRequestInfo>,
    override val totalPages: Int,
    override val totalItems: Int,
    override val currentPage: Int
): MergeRequestApi.SearchResult

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/PipelineImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.DateTime
import net.ntworld.mergeRequest.Pipeline
import net.ntworld.mergeRequest.PipelineStatus

data class PipelineImpl(
    override val id: String,
    override val hash: String,
    override val ref: String,
    override val status: PipelineStatus,
    override val url: String,
    override val createdAt: DateTime?,
    override val updatedAt: DateTime?
) : Pipeline

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/ProjectImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.Project
import net.ntworld.mergeRequest.ProjectVisibility
import net.ntworld.mergeRequest.ProviderInfo

data class ProjectImpl(
    override val id: String,
    override val provider: ProviderInfo,
    override val name: String,
    override val path: String,
    override val url: String,
    override val avatarUrl: String,
    override val visibility: ProjectVisibility,
    override val repositoryHttpUrl: String,
    override val repositorySshUrl: String
): Project

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/ProviderDataImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.*
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.gitlab.Gitlab
import net.ntworld.mergeRequestIntegration.provider.gitlab.GitlabUtil

data class ProviderDataImpl(
    override val id: String,
    override val key: String,
    override val name: String,
    override val info: ProviderInfo,
    override val credentials: ApiCredentials,
    override val project: Project,
    override val currentUser: User,
    override val repository: String,
    override val errorMessage: String?,
    override val status: ProviderStatus
) : ProviderData {
    override val hasApprovalFeature: Boolean
        get() {
            if (this.info.id != Gitlab.id) {
                return true
            }
            if (GitlabUtil.hasMergeApprovalFeature(credentials)) {
                return true
            }
            return false
        }

    override val hasAssigneeFeature: Boolean
        get() {
            if (this.info.id == Gitlab.id) {
                return true
            }
            return false
        }
}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/UserImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.DateTime
import net.ntworld.mergeRequest.User
import net.ntworld.mergeRequest.UserStatus

data class UserImpl(
    override val id: String,
    override val name: String,
    override val username: String,
    override val avatarUrl: String?,
    override val url: String,
    override val status: UserStatus,
    override val email: String,
    override val createdAt: DateTime
) : User

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/UserInfoImpl.kt
================================================
package net.ntworld.mergeRequestIntegration.internal

import net.ntworld.mergeRequest.UserInfo
import net.ntworld.mergeRequest.UserStatus

data class UserInfoImpl(
    override val id: String,
    override val name: String,
    override val username: String,
    override val avatarUrl: String?,
    override val url: String,
    override val status: UserStatus
) : UserInfo {
    companion object {
        val None = UserInfoImpl(
            id = "",
            name = "<None>",
            username = "",
            avatarUrl = "",
            url = "",
            status = UserStatus.ACTIVE
        )
    }
}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/DraftCommentApi.kt
================================================
package net.ntworld.mergeRequestIntegration.provider

import net.ntworld.mergeRequest.Comment
import net.ntworld.mergeRequest.CommentPosition
import net.ntworld.mergeRequest.Project
import net.ntworld.mergeRequest.api.CommentApi
import net.ntworld.mergeRequest.api.DraftCommentStorage

class DraftCommentApi(private val api: CommentApi, private val storage: DraftCommentStorage): CommentApi {
    override fun getAll(project: Project, mergeRequestId: String): List<Comment> {
        val result = mutableListOf<Comment>()
        result.addAll(api.getAll(project, mergeRequestId))
        result.addAll(storage.getAll(project, mergeRequestId))

        return result
    }

    override fun create(
        project: Project,
        mergeRequestId: String,
        body: String,
        position: CommentPosition?,
        isDraft: Boolean
    ): String? {
        if (isDraft) {
            return storage.create(project, mergeRequestId, body, position)
        }
        return api.create(project, mergeRequestId, body, position, false)
    }

    override fun reply(project: Project, mergeRequestId: String, repliedComment: Comment, body: String): String? {
        return api.reply(project, mergeRequestId, repliedComment, body)
    }

    override fun delete(project: Project, mergeRequestId: String, comment: Comment) {
        if (comment.isDraft) {
            return storage.delete(project, mergeRequestId, comment)
        }
        return api.delete(project, mergeRequestId, comment)
    }

    override fun resolve(project: Project, mergeRequestId: String, comment: Comment) {
        if (!comment.isDraft) {
            return api.resolve(project, mergeRequestId, comment)
        }
    }

    override fun unresolve(project: Project, mergeRequestId: String, comment: Comment) {
        if (!comment.isDraft) {
            return api.unresolve(project, mergeRequestId, comment)
        }
    }

    override fun update(project: Project, mergeRequestId: String, comment: Comment, body: String) {
        if (!comment.isDraft) {
            return api.update(project, mergeRequestId, comment, body)
        }
        return storage.update(project, mergeRequestId, comment, body)
    }

    override fun getDraftCount(project: Project, mergeRequestId: String): Int {
        return storage.getAll(project, mergeRequestId).count()
    }

    override fun publishAllDraftComments(project: Project, mergeRequestId: String) {
        val comments = storage.getAll(project, mergeRequestId)
        for (comment in comments) {
            publishDraftComment(project, mergeRequestId, comment)
        }
    }

    override fun publishDraftComments(project: Project, mergeRequestId: String, commentIds: List<String>) {
        for (commentId in commentIds) {
            val comment = storage.findById(project, mergeRequestId, commentId)
            if (null !== comment) {
                publishDraftComment(project, mergeRequestId, comment)
            }
        }
    }

    private fun publishDraftComment(project: Project, mergeRequestId: String, comment: Comment) {
        if (comment.isDraft) {
            api.create(project, mergeRequestId, comment.body, comment.position, false)
            storage.delete(project, mergeRequestId, comment)
        }
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/FuelClient.kt
================================================
package net.ntworld.mergeRequestIntegration.provider

import com.github.kittinunf.fuel.core.*
import com.github.kittinunf.result.Result
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import net.ntworld.mergeRequest.api.ApiCredentials
import java.security.cert.X509Certificate
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager

abstract class FuelClient (
    private val credentials: ApiCredentials
) {
    val json = Json(JsonConfiguration.Stable.copy(strictMode = false))

    protected abstract fun injectAuthentication(httpRequest: Request): Request

    protected fun makeRequestFactory(): RequestFactory.Convenience {
        if (credentials.ignoreSSLCertificateErrors) {
            return FuelManager().apply {
                val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
                    override fun getAcceptedIssuers(): Array<X509Certificate>? = null
                    override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) = Unit
                    override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) = Unit
                })

                socketFactory = SSLContext.getInstance("SSL").apply {
                    init(null, trustAllCerts, java.security.SecureRandom())
                }.socketFactory

                hostnameVerifier = HostnameVerifier { _, _ -> true }
            }
        }
        return FuelManager()
    }

    fun postJson(url: String, parameters: Parameters? = null): String {
        return executeRequest(makeRequestFactory().post(url, parameters))
    }

    fun deleteJson(url: String, parameters: Parameters? = null): String {
        return executeRequest(makeRequestFactory().delete(url, parameters))
    }

    fun putJson(url: String, parameters: Parameters? = null): String {
        return executeRequest(makeRequestFactory().put(url, parameters))
    }

    fun getJson(url: String, parameters: Parameters? = null): String {
        return executeRequest(makeRequestFactory().get(url, parameters))
    }

    protected fun executeRequest(request: Request) : String {
        val httpRequest = injectAuthentication(request)
        val (_, response, result) = httpRequest.responseString()
        return when (result) {
            is Result.Success -> {
                result.value
            }
            is Result.Failure -> {
                throw HttpException(response.statusCode, result.error.message ?: "Unknown")
            }
        }
    }

}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/MemoryCache.kt
================================================
package net.ntworld.mergeRequestIntegration.provider

import net.ntworld.mergeRequest.api.Cache
import net.ntworld.mergeRequest.api.CacheNotFoundException
import net.ntworld.mergeRequestIntegration.exception.InvalidCacheKeyException
import net.ntworld.mergeRequestIntegration.exception.InvalidTTLException
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import org.joda.time.LocalDateTime

class MemoryCache(ttl: Int? = null) : Cache {
    private val data = mutableMapOf<String, CachedData>()

    override var defaultTTL: Int = 0
        private set

    init {
        defaultTTL = if (null === ttl) {
            60000
        } else {
            if (ttl <= 0) {
                throw InvalidTTLException()
            }
            ttl
        }
    }

    @Suppress("UNCHECKED_CAST")
    override fun <T> get(key: String): T {
        assertKeyIsValid(key)
        if (!data.containsKey(key)) {
            throw CacheNotFoundException()
        }

        val cachedItem = data[key]
        if (null === cachedItem || isExpired(cachedItem.expired)) {
            throw CacheNotFoundException()
        }
        return cachedItem.value as T
    }

    override fun has(key: String): Boolean {
        assertKeyIsValid(key)
        if (!data.containsKey(key)) {
            return false
        }
        val cachedItem = data[key]
        return null !== cachedItem && !isExpired(cachedItem.expired)
    }

    override fun remove(key: String) {
        assertKeyIsValid(key)
        data.remove(key)
    }

    override fun put(key: String, value: Any, ttl: Int) {
        data[key] = CachedData(value, now() + ttl)
    }

    override fun isExpiredAfter(key: String, datetime: DateTime): Boolean {
        assertKeyIsValid(key)
        val cachedItem = data[key]
        if (null === cachedItem) {
            throw CacheNotFoundException()
        }
        return cachedItem.expired < toUtc(datetime).millis
    }

    private fun assertKeyIsValid(key: String) {
        if (key.trim().isEmpty()) {
            throw InvalidCacheKeyException("Key is invalid")
        }
    }

    internal fun isExpired(expired: Long): Boolean {
        return expired < now()
    }

    internal fun now(): Long {
        return toUtc(DateTime.now()).millis
    }

    private fun toUtc(datetime: DateTime): DateTime {
        return LocalDateTime(datetime).toDateTime(DateTimeZone.UTC)
    }

    private data class CachedData(
        val value: Any,
        val expired: Long
    )
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/MemoryDraftCommentStorage.kt
================================================
package net.ntworld.mergeRequestIntegration.provider

import net.ntworld.mergeRequest.Comment
import net.ntworld.mergeRequest.CommentPosition
import net.ntworld.mergeRequest.Project
import net.ntworld.mergeRequest.UserInfo
import net.ntworld.mergeRequest.api.DraftCommentStorage
import net.ntworld.mergeRequestIntegration.internal.CommentImpl
import net.ntworld.mergeRequestIntegration.util.DateTimeUtil
import org.joda.time.DateTime
import java.util.*

class MemoryDraftCommentStorage(private val currentUser: UserInfo) : DraftCommentStorage {
    private val data = mutableMapOf<String, MutableMap<String, Comment>>()

    private fun getMutableMap(project: Project, mergeRequestId: String): MutableMap<String, Comment> {
        val key = project.id + "-" + mergeRequestId
        if (!data.contains(key)) {
            data[key] = mutableMapOf()
        }
        return data[key] as MutableMap<String, Comment>
    }

    override fun getAll(project: Project, mergeRequestId: String): List<Comment> {
        return getMutableMap(project, mergeRequestId).values.toList()
    }

    override fun findById(project: Project, mergeRequestId: String, commentId: String): Comment? {
        val mutableMap = getMutableMap(project, mergeRequestId)
        return mutableMap[commentId]
    }

    override fun create(project: Project, mergeRequestId: String, body: String, position: CommentPosition?): String? {
        val mutableMap = getMutableMap(project, mergeRequestId)
        val id = "tmp:${UUID.randomUUID()}"
        mutableMap[id] = CommentImpl(
            id = id,
            parentId = UUID.randomUUID().toString(),
            replyId = "",
            body = body,
            position = position,
            createdAt = DateTimeUtil.fromDate(DateTime.now().toDate()),
            updatedAt = DateTimeUtil.fromDate(DateTime.now().toDate()),
            resolvable = false,
            resolved = false,
            resolvedBy = null,
            author = currentUser,
            isDraft = true
        )
        return id
    }

    override fun update(project: Project, mergeRequestId: String, comment: Comment, body: String) {
        if (!comment.isDraft) {
            return
        }
        val mutableMap = getMutableMap(project, mergeRequestId)
        if (mutableMap.contains(comment.id)) {
            mutableMap[comment.id] = CommentImpl(
                id = comment.id,
                parentId = comment.parentId,
                replyId = "",
                body = body,
                position = comment.position,
                createdAt = comment.createdAt,
                updatedAt = DateTimeUtil.fromDate(DateTime.now().toDate()),
                resolvable = false,
                resolved = false,
                resolvedBy = null,
                author = currentUser,
                isDraft = true
            )
        }
    }

    override fun delete(project: Project, mergeRequestId: String, comment: Comment) {
        if (!comment.isDraft) {
            return
        }
        val mutableMap = getMutableMap(project, mergeRequestId)
        mutableMap.remove(comment.id)
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/MergeRequestApiDecorator.kt
================================================
package net.ntworld.mergeRequestIntegration.provider

import net.ntworld.mergeRequest.api.MergeRequestApi

open class MergeRequestApiDecorator(
    private val wrappee: MergeRequestApi
): MergeRequestApi by wrappee


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/ProviderException.kt
================================================
package net.ntworld.mergeRequestIntegration.provider

import net.ntworld.foundation.Error

class ProviderException(val error: Error) : Exception(error.message)


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/Transformer.kt
================================================
package net.ntworld.mergeRequestIntegration.provider

interface Transformer<T, R> {
    fun transform(input: T): R
}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/Github.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github

import net.ntworld.mergeRequest.Comment
import net.ntworld.mergeRequest.ProviderInfo

object Github : ProviderInfo {

    override val id: String = "github"

    override val name: String = "Github"

    override val iconPath: String = "/icons/github.svg"

    override val icon2xPath: String = "/icons/github@2x.svg"

    override val icon3xPath: String = "/icons/github@3x.svg"

    override val icon4xPath: String = "/icons/github@4x.svg"

    override fun createCommentUrl(mergeRequestUrl: String, comment: Comment): String {
        return "$mergeRequestUrl#comment_${comment.id}"
    }

    override fun formatMergeRequestId(mergeRequestId: String): String {
        return mergeRequestId
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubApiProvider.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.ProviderInfo
import net.ntworld.mergeRequest.UserInfo
import net.ntworld.mergeRequest.api.*

class GithubApiProvider(
    private val infrastructure: Infrastructure,
    override val credentials: ApiCredentials,
    override val cache: Cache
) : ApiProvider {
    private val myMergeRequestApi = GithubMergeRequestApi(infrastructure, credentials)

    override val info: ProviderInfo = Github

    override val user: UserApi = GithubUserApi(infrastructure, credentials)

    override val mergeRequest: MergeRequestApi = myMergeRequestApi

    override val project: ProjectApi = GithubProjectApi(infrastructure, credentials)

    override val comment: CommentApi
        get() = TODO("not implemented")

    override val commit: CommitApi
        get() = TODO("Not yet implemented")

    override fun setOptions(options: ApiOptions) {
    }

    override fun initialize(currentUser: UserInfo) {
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubClient.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github

import net.ntworld.foundation.Error
import net.ntworld.foundation.Request
import net.ntworld.foundation.Response
import net.ntworld.mergeRequest.api.ApiCredentials
import org.kohsuke.github.GitHub
import org.kohsuke.github.GitHubBuilder
import java.io.IOException

object GithubClient {
    private fun makeGitHub(credentials: ApiCredentials) : GitHub {
        return GitHubBuilder()
            .withEndpoint(credentials.url)
            .withPassword(credentials.login, credentials.token)
            .build()
    }

    operator fun <T, R: Response> invoke(
        request: T,
        execute: (GitHub.(T) -> R),
        failed: ((Error) -> R)
    ) : R where T : Request<R>, T : GithubRequest {
        return try {
            execute.invoke(
                makeGitHub(request.credentials),
                request
            )
        } catch (exception: IOException) {
            failed.invoke(
                GithubFailedRequestError(
                    exception.message ?: "Failed request",
                    400
                )
            )
        }
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubFailedRequestError.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github

import net.ntworld.foundation.Error

data class GithubFailedRequestError(
    override val message: String,
    override val code: Int
): Error {
    override val type: String = "net.ntworld.mergeRequestIntegration.provider.github.GithubFailedRequestError"
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubFuelClient.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github

import com.github.kittinunf.fuel.core.HttpException
import com.github.kittinunf.fuel.core.Request
import com.github.kittinunf.fuel.core.extensions.authentication
import net.ntworld.foundation.Error
import net.ntworld.foundation.Response
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.FuelClient

class GithubFuelClient private constructor(
    private val credentials: ApiCredentials
) : FuelClient(credentials) {
    val searchIssuesUrl = "${credentials.url}/search/issues"

    override fun injectAuthentication(httpRequest: Request): Request {
        return httpRequest.authentication().basic(credentials.login, credentials.token)
    }

    companion object {
        operator fun <T, R : Response> invoke(
            request: T,
            execute: (GithubFuelClient.(T) -> R),
            failed: ((Error) -> R)
        ): R where T : net.ntworld.foundation.Request<R>, T : GithubRequest {
            return try {
                val client = GithubFuelClient(request.credentials)
                execute.invoke(client, request)
            } catch (exception: HttpException) {
                failed.invoke(
                    GithubFailedRequestError(
                        exception.message ?: "Failed request",
                        500
                    )
                )
            }
        }
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubMergeRequestApi.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.*
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequest.api.MergeRequestApi
import net.ntworld.mergeRequest.api.MergeRequestOrdering
import net.ntworld.mergeRequest.query.GetMergeRequestFilter
import net.ntworld.mergeRequestIntegration.internal.MergeRequestSearchResultImpl
import net.ntworld.mergeRequestIntegration.provider.github.request.GithubSearchPRsRequest
import net.ntworld.mergeRequestIntegration.provider.github.transformer.GithubSearchPullRequestItemTransformer
import net.ntworld.mergeRequestIntegration.provider.github.vo.GithubProjectId
import net.ntworld.mergeRequestIntegration.provider.github.vo.GithubUserId
import kotlin.math.ceil

class GithubMergeRequestApi(
    private val infrastructure: Infrastructure,
    private val credentials: ApiCredentials
) : MergeRequestApi {

    override fun find(projectId: String, mergeRequestId: String): MergeRequest? {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun approve(projectId: String, mergeRequestId: String, sha: String) {
    }

    override fun unapprove(projectId: String, mergeRequestId: String) {
    }

    override fun findApproval(projectId: String, mergeRequestId: String): Approval {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun getPipelines(projectId: String, mergeRequestId: String): List<Pipeline> {
        // TODO: get pipelines
        return listOf()
    }

    override fun getCommits(projectId: String, mergeRequestId: String): List<Commit> {
        // TODO: get commits
        return listOf()
    }

    override fun getChanges(projectId: String, mergeRequestId: String): List<Change> {
        TODO("Not yet implemented")
    }

    override fun search(
        projectId: String,
        currentUserId: String,
        filterBy: GetMergeRequestFilter,
        orderBy: MergeRequestOrdering,
        page: Int,
        itemsPerPage: Int
    ): MergeRequestApi.SearchResult {
        val (sort, order) = resolveOrderAndSort(orderBy)
        val reviewer = if (filterBy.approverIds.isNotEmpty() && filterBy.approverIds[0].isNotEmpty()) {
            GithubUserId.parseLogin(filterBy.approverIds[0])
        } else ""
        val out = infrastructure.serviceBus() process GithubSearchPRsRequest(
            credentials = credentials,
            repo = GithubProjectId.parseFullName(credentials.projectId),
            state = when (filterBy.state) {
                MergeRequestState.ALL -> ""
                MergeRequestState.OPENED -> PULL_REQUEST_STATE_OPEN
                MergeRequestState.CLOSED -> PULL_REQUEST_STATE_CLOSED
                MergeRequestState.MERGED -> PULL_REQUEST_STATE_MERGED
            },
            author = if (filterBy.authorId.isNotEmpty()) GithubUserId.parseLogin(filterBy.authorId) else "",
            assignee = if (filterBy.assigneeId.isNotEmpty()) GithubUserId.parseLogin(filterBy.assigneeId) else "",
            reviewer = reviewer,
            term = filterBy.search,
            sort = sort,
            order = order,
            page = page,
            perPage = itemsPerPage
        )

        return if (out.hasError()) {
            MergeRequestSearchResultImpl(listOf(), 0, 0, 0)
        } else {
            val response = out.getResponse()
            val transformer = GithubSearchPullRequestItemTransformer(projectId)
            MergeRequestSearchResultImpl(
                data = response.result.items.map { transformer.transform(it) },
                totalItems = response.result.totalCount,
                totalPages = ceil(response.result.totalCount.toDouble() / itemsPerPage).toInt(),
                currentPage = page
            )
        }
    }

    private fun resolveState(state: MergeRequestState): String = when(state) {
        MergeRequestState.ALL -> "all"
        MergeRequestState.OPENED -> "open"
        MergeRequestState.CLOSED -> "closed"
        MergeRequestState.MERGED -> "merged"
    }

    private fun resolveOrderAndSort(orderBy: MergeRequestOrdering): Pair<String, String> {
        return when (orderBy) {
            MergeRequestOrdering.RECENTLY_UPDATED -> Pair("updated", "desc")
            MergeRequestOrdering.NEWEST -> Pair("created", "desc")
            MergeRequestOrdering.OLDEST -> Pair("created", "asc")
        }
    }

    override fun findOrFail(projectId: String, mergeRequestId: String): MergeRequest {
        val mergeRequest = find(projectId, mergeRequestId)
        if (null === mergeRequest) {
            throw Exception("MergeRequest $mergeRequestId not found.")
        }
        return mergeRequest
    }

}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubProjectApi.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.Project
import net.ntworld.mergeRequest.UserInfo
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequest.api.ProjectApi
import net.ntworld.mergeRequestIntegration.provider.github.request.GithubFindRepositoryRequest
import net.ntworld.mergeRequestIntegration.provider.github.transformer.GithubRepositoryTransformer
import net.ntworld.mergeRequestIntegration.provider.github.vo.GithubProjectId

class GithubProjectApi(
    private val infrastructure: Infrastructure,
    private val credentials: ApiCredentials
) : ProjectApi {

    override fun find(projectId: String): Project? {
        val out = infrastructure.serviceBus() process GithubFindRepositoryRequest(
            credentials, repositoryId = GithubProjectId.parseId(projectId).toString()
        )

        return if (out.hasError()) {
            null
        } else {
            GithubRepositoryTransformer.transform(out.getResponse().repository)
        }
    }

    override fun getMembers(projectId: String): List<UserInfo> {
        // TODO: Get members of project
        return listOf()
    }

}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubRequest.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github

import net.ntworld.mergeRequest.api.ApiCredentials

interface GithubRequest {
    val credentials: ApiCredentials
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubUserApi.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.User
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequest.api.UserApi
import net.ntworld.mergeRequestIntegration.provider.github.request.GithubFindCurrentUserRequest
import net.ntworld.mergeRequestIntegration.provider.github.transformer.GithubUserTransformer

class GithubUserApi(
    private val infrastructure: Infrastructure,
    private val credentials: ApiCredentials
) : UserApi {

    override fun me(): User {
        val response = infrastructure.serviceBus() process GithubFindCurrentUserRequest(credentials) ifError {
            throw Exception("Cannot find info of current user.")
        }
        return GithubUserTransformer.transform(response.user)
    }

}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubUtil.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github

object GithubUtil

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/_const.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github

const val PULL_REQUEST_STATE_OPEN = "open"
const val PULL_REQUEST_STATE_CLOSED = "closed"
const val PULL_REQUEST_STATE_MERGED = "merged"

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/model/PullRequestSearchItem.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.model

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class PullRequestSearchItem(
    val id: Long,

    val number: Int,

    @SerialName("node_id")
    val nodeId: String,

    val title: String,

    val body: String,

    @SerialName("html_url")
    val htmlUrl: String,

    val state: String,

    @SerialName("created_at")
    val createdAt: String,

    @SerialName("updated_at")
    val updatedAt: String
)

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/model/SearchPullRequestResult.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.model

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class SearchPullRequestResult(
    @SerialName("total_count")
    val totalCount: Int,

    @SerialName("incomplete_results")
    val incompleteResults: Boolean,

    val items: List<PullRequestSearchItem>
)

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/request/GithubFindCurrentUserRequest.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.github.GithubRequest
import net.ntworld.mergeRequestIntegration.provider.github.response.GithubFindUserResponse

data class GithubFindCurrentUserRequest(
    override val credentials: ApiCredentials
): GithubRequest, Request<GithubFindUserResponse>

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/request/GithubFindRepositoryRequest.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.github.GithubRequest
import net.ntworld.mergeRequestIntegration.provider.github.response.GithubFindRepositoryResponse

data class GithubFindRepositoryRequest(
    override val credentials: ApiCredentials,
    val repositoryId: String
): GithubRequest, Request<GithubFindRepositoryResponse>



================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/request/GithubSearchPRsRequest.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.github.GithubRequest
import net.ntworld.mergeRequestIntegration.provider.github.response.GithubSearchPRsResponse

data class GithubSearchPRsRequest(
    override val credentials: ApiCredentials,
    val repo: String,
    val state: String,
    val author: String,
    val reviewer: String,
    val assignee: String,
    val term: String,
    val sort: String,
    val order: String,
    val page: Int = 0,
    val perPage: Int = 10
) : GithubRequest, Request<GithubSearchPRsResponse>


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/request/GithubSearchRepositoriesRequest.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.github.GithubRequest
import net.ntworld.mergeRequestIntegration.provider.github.response.GithubSearchRepositoriesResponse

data class GithubSearchRepositoriesRequest(
    override val credentials: ApiCredentials,
    val term: String
) : GithubRequest, Request<GithubSearchRepositoriesResponse>

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/requestHandler/GithubFindCurrentUserRequestHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.requestHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.RequestHandler
import net.ntworld.mergeRequestIntegration.provider.github.GithubClient
import net.ntworld.mergeRequestIntegration.provider.github.request.GithubFindCurrentUserRequest
import net.ntworld.mergeRequestIntegration.provider.github.response.GithubFindUserResponse
import org.kohsuke.github.GHUser

@Handler
class GithubFindCurrentUserRequestHandler : RequestHandler<GithubFindCurrentUserRequest, GithubFindUserResponse> {
    override fun handle(request: GithubFindCurrentUserRequest): GithubFindUserResponse = GithubClient(
        request = request,
        execute = {
            GithubFindUserResponse(error = null, user = myself)
        },
        failed = {
            GithubFindUserResponse(error = it, user = GHUser())
        }
    )
}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/requestHandler/GithubFindRepositoryRequestHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.requestHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.RequestHandler
import net.ntworld.mergeRequestIntegration.provider.github.GithubClient
import net.ntworld.mergeRequestIntegration.provider.github.GithubUtil
import net.ntworld.mergeRequestIntegration.provider.github.request.GithubFindRepositoryRequest
import net.ntworld.mergeRequestIntegration.provider.github.response.GithubFindRepositoryResponse
import org.kohsuke.github.GHRepository

@Handler
class GithubFindRepositoryRequestHandler : RequestHandler<GithubFindRepositoryRequest, GithubFindRepositoryResponse> {
    override fun handle(request: GithubFindRepositoryRequest): GithubFindRepositoryResponse = GithubClient(
        request = request,
        execute = {
            val repository = this.getRepositoryById(request.repositoryId)

            GithubFindRepositoryResponse(error = null, repository = repository)
        },
        failed = {
            GithubFindRepositoryResponse(error = it, repository = GHRepository())
        }
    )
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/requestHandler/GithubSearchPRsRequestHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.requestHandler

import kotlinx.serialization.serializer
import net.ntworld.foundation.Handler
import net.ntworld.foundation.RequestHandler
import net.ntworld.mergeRequestIntegration.provider.github.GithubFuelClient
import net.ntworld.mergeRequestIntegration.provider.github.model.SearchPullRequestResult
import net.ntworld.mergeRequestIntegration.provider.github.request.GithubSearchPRsRequest
import net.ntworld.mergeRequestIntegration.provider.github.response.GithubSearchPRsResponse

@Handler
class GithubSearchPRsRequestHandler : RequestHandler<GithubSearchPRsRequest, GithubSearchPRsResponse> {

    /**
     * Because there is no way to do pagination manually with the library https://github.com/github-api/github-api/
     * then I have to change the contract & use a custom client.
     */
    override fun handle(request: GithubSearchPRsRequest): GithubSearchPRsResponse = GithubFuelClient(
        request = request,
        execute = {
            val q = mutableListOf<String>()
            q.add("repo:${request.repo}")
            q.add("is:pr")
            q.add("state:${request.state}")
            if (request.author.isNotEmpty()) {
                q.add("author:${request.author}")
            }
            if (request.reviewer.isNotEmpty()) {
                q.add("reviewed-by:${request.reviewer}")
            }
            if (request.assignee.isNotEmpty()) {
                q.add("assignee:${request.assignee}")
            }
            if (request.term.trim().isNotEmpty()) {
                q.add(request.term.trim())
            }

            val params: MutableList<Pair<String, Any?>> = mutableListOf(
                Pair("q", q.joinToString(" ")),
                Pair("sort", request.sort),
                Pair("order", request.order),
                Pair("page", request.page),
                Pair("per_page", request.perPage)
            )

            val input = this.getJson(searchIssuesUrl, params)
            GithubSearchPRsResponse(error = null, result = json.parse(
                SearchPullRequestResult.serializer(),
                input
            ))
        },
        failed = {
            GithubSearchPRsResponse(error = it, result = SearchPullRequestResult(
                totalCount = 0,
                incompleteResults = false,
                items = listOf()
            ))
        }
    )
}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/requestHandler/GithubSearchRepositoriesRequestHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.requestHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.RequestHandler
import net.ntworld.mergeRequestIntegration.provider.github.GithubClient
import net.ntworld.mergeRequestIntegration.provider.github.request.GithubSearchRepositoriesRequest
import net.ntworld.mergeRequestIntegration.provider.github.response.GithubSearchRepositoriesResponse
import org.kohsuke.github.GitHubBuilder

@Handler
class GithubSearchRepositoriesRequestHandler : RequestHandler<GithubSearchRepositoriesRequest, GithubSearchRepositoriesResponse> {

    override fun handle(request: GithubSearchRepositoriesRequest): GithubSearchRepositoriesResponse = GithubClient(
        request = request,
        execute = {
            val search = this.searchRepositories().q(request.term)
            val pager = search.list().withPageSize(10)
            val data = pager.iterator().nextPage()
            GithubSearchRepositoriesResponse(error = null, repositories = data)
        },
        failed = {
            GithubSearchRepositoriesResponse(error = it, repositories = listOf())
        }
    )

}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/response/GithubFindRepositoryResponse.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.response

import net.ntworld.foundation.Error
import net.ntworld.foundation.Response
import org.kohsuke.github.GHRepository

data class GithubFindRepositoryResponse(
    override val error: Error?,
    val repository: GHRepository
) : Response


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/response/GithubFindUserResponse.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.response

import net.ntworld.foundation.Error
import net.ntworld.foundation.Response
import org.kohsuke.github.GHUser

data class GithubFindUserResponse(
    override val error: Error?,
    val user: GHUser
): Response

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/response/GithubSearchPRsResponse.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.response

import net.ntworld.foundation.Error
import net.ntworld.foundation.Response
import net.ntworld.mergeRequestIntegration.provider.github.model.PullRequestSearchItem
import net.ntworld.mergeRequestIntegration.provider.github.model.SearchPullRequestResult

data class GithubSearchPRsResponse(
    override val error: Error?,
    val result: SearchPullRequestResult
): Response


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/response/GithubSearchRepositoriesResponse.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.response

import net.ntworld.foundation.Error
import net.ntworld.foundation.Response
import org.kohsuke.github.GHRepository

data class GithubSearchRepositoriesResponse(
    override val error: Error?,
    val repositories: List<GHRepository>
): Response

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/transformer/GithubRepositoryTransformer.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.transformer

import net.ntworld.mergeRequest.Project
import net.ntworld.mergeRequest.ProjectVisibility
import net.ntworld.mergeRequestIntegration.internal.ProjectImpl
import net.ntworld.mergeRequestIntegration.provider.github.Github
import net.ntworld.mergeRequestIntegration.provider.Transformer
import net.ntworld.mergeRequestIntegration.provider.github.vo.GithubProjectId
import org.kohsuke.github.GHRepository

object GithubRepositoryTransformer :
    Transformer<GHRepository, Project> {
    override fun transform(input: GHRepository): Project = ProjectImpl(
        // Unlike gitlab, github works based on :owner/:repo rather than id
        // So to keep everything works as expected, we have to add :owner/:repo information
        // Please use GithubProjectId value-object to work with generate/parsing user id
        id = GithubProjectId(id = input.id, owner = input.ownerName, repo = input.name).getValue(),
        provider = Github,
        name = input.name,
        path = input.fullName,
        url = input.url.toString(),
        visibility = if (input.isPrivate) ProjectVisibility.PRIVATE else ProjectVisibility.PUBLIC,
        avatarUrl = "",
        repositoryHttpUrl = input.htmlUrl.toString(),
        repositorySshUrl = input.sshUrl
    )
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/transformer/GithubSearchPullRequestItemTransformer.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.transformer

import net.ntworld.mergeRequest.MergeRequestInfo
import net.ntworld.mergeRequest.MergeRequestState
import net.ntworld.mergeRequestIntegration.internal.MergeRequestInfoImpl
import net.ntworld.mergeRequestIntegration.provider.Transformer
import net.ntworld.mergeRequestIntegration.provider.github.Github
import net.ntworld.mergeRequestIntegration.provider.github.PULL_REQUEST_STATE_CLOSED
import net.ntworld.mergeRequestIntegration.provider.github.PULL_REQUEST_STATE_MERGED
import net.ntworld.mergeRequestIntegration.provider.github.PULL_REQUEST_STATE_OPEN
import net.ntworld.mergeRequestIntegration.provider.github.model.PullRequestSearchItem
import net.ntworld.mergeRequestIntegration.provider.github.vo.GithubMergeRequestId

class GithubSearchPullRequestItemTransformer(
    private val projectId: String
) : Transformer<PullRequestSearchItem, MergeRequestInfo> {
    override fun transform(input: PullRequestSearchItem): MergeRequestInfo = MergeRequestInfoImpl(
        id = GithubMergeRequestId(input.id, input.number, input.nodeId).getValue(),
        provider = Github.id,
        projectId = projectId,
        title = input.title,
        description = input.body,
        url = input.htmlUrl,
        state = when (input.state) {
            PULL_REQUEST_STATE_OPEN -> MergeRequestState.OPENED
            PULL_REQUEST_STATE_MERGED -> MergeRequestState.MERGED
            PULL_REQUEST_STATE_CLOSED -> MergeRequestState.CLOSED
            else -> MergeRequestState.CLOSED
        },
        createdAt = input.createdAt,
        updatedAt = input.updatedAt
    )
}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/transformer/GithubUserTransformer.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.transformer

import net.ntworld.mergeRequest.User
import net.ntworld.mergeRequest.UserStatus
import net.ntworld.mergeRequestIntegration.DEFAULT_DATETIME
import net.ntworld.mergeRequestIntegration.internal.UserImpl
import net.ntworld.mergeRequestIntegration.provider.Transformer
import net.ntworld.mergeRequestIntegration.provider.github.vo.GithubUserId
import net.ntworld.mergeRequestIntegration.util.DateTimeUtil
import org.kohsuke.github.GHUser

object GithubUserTransformer : Transformer<GHUser, User> {
    override fun transform(input: GHUser): User = UserImpl(
        // Unlike gitlab, github works based on login rather than id
        // So to keep everything works as expected, we have to add username into id information
        // Please use GithubUserId value-object to work with generate/parsing user id
        id = GithubUserId(input.id, input.login).getValue(),
        name = if (input.name.isNullOrBlank()) input.login else input.name,
        username = input.login,
        avatarUrl = input.avatarUrl,
        url = input.htmlUrl.toString(),
        status = UserStatus.ACTIVE,
        email = if (input.email.isNullOrBlank()) "" else input.email,
        createdAt = if (null === input.createdAt) DEFAULT_DATETIME else DateTimeUtil.fromDate(input.createdAt)
    )
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/vo/GithubMergeRequestId.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.vo

data class GithubMergeRequestId(
    val id: Long,
    val number: Int,
    val nodeId: String
) {
    fun getValue() : String {
        return "$id:$number:$nodeId"
    }

    companion object {
        fun parse(input: String): GithubMergeRequestId {
            val parts = input.split(":")
            if (parts.size != 3) {
                throw Exception("Invalid Github mergeRequestId")
            }
            return GithubMergeRequestId(
                id = parts[0].toLong(),
                number = parts[1].toInt(),
                nodeId = parts[2]
            )
        }

        fun parseId(mergeRequestId: String) : Long {
            return parse(mergeRequestId).id
        }

        fun parseNumber(mergeRequestId: String) : Int {
            return parse(mergeRequestId).number
        }

        fun parseNodeId(mergeRequestId: String) : String {
            return parse(mergeRequestId).nodeId
        }
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/vo/GithubProjectId.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.vo

data class GithubProjectId(
    val id: Long,
    val owner: String,
    val repo: String
) {
    fun getValue() : String {
        return "$id:$owner/$repo"
    }

    companion object {
        fun parse(input: String): GithubProjectId {
            val parts = input.split(":")
            if (parts.size != 2) {
                throw Exception("Invalid Github projectId")
            }
            val info = parts[1].split("/")
            if (info.size != 2) {
                throw Exception("Invalid Github projectId")
            }
            return GithubProjectId(
                id = parts[0].toLong(), owner = info[0], repo = info[1]
            )
        }

        fun parseId(projectId: String) : Long {
            return parse(projectId).id
        }

        fun parseFullName(projectId: String): String {
            val data = parse(projectId)
            return "${data.owner}/${data.repo}"
        }

        fun parseOwner(projectId: String) : String {
            return parse(projectId).owner
        }

        fun parseRepo(projectId: String): String {
            return parse(projectId).repo
        }
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/vo/GithubUserId.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.github.vo

data class GithubUserId(
    val id: Long,
    val login: String
) {
    fun getValue() : String {
        return "$id:$login"
    }

    companion object {
        fun parse(input: String): GithubUserId {
            val parts = input.split(":")
            if (parts.size != 2) {
                throw Exception("Invalid Github userId")
            }
            return GithubUserId(
                id = parts[0].toLong(), login = parts[1]
            )
        }

        fun parseId(input: String): Long {
            return parse(input).id
        }

        fun parseLogin(input: String): String {
            return parse(input).login
        }
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/Gitlab.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.mergeRequest.Comment
import net.ntworld.mergeRequest.ProviderInfo

object Gitlab : ProviderInfo {

    override val id: String = "gitlab"

    override val name: String = "GitLab"

    override val iconPath: String = "/icons/gitlab.svg"

    override val icon2xPath: String = "/icons/gitlab@2x.svg"

    override val icon3xPath: String = "/icons/gitlab@3x.svg"

    override val icon4xPath: String = "/icons/gitlab@4x.svg"

    @Synchronized
    override fun createCommentUrl(mergeRequestUrl: String, comment: Comment): String {
        return "$mergeRequestUrl#note_${comment.id}"
    }

    @Synchronized
    override fun formatMergeRequestId(mergeRequestId: String): String {
        return "!$mergeRequestId"
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabApiProvider.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.ProviderInfo
import net.ntworld.mergeRequest.UserInfo
import net.ntworld.mergeRequest.api.*
import net.ntworld.mergeRequestIntegration.provider.DraftCommentApi
import net.ntworld.mergeRequestIntegration.provider.MemoryDraftCommentStorage

class GitlabApiProvider(
    private val infrastructure: Infrastructure,

    override val credentials: ApiCredentials,

    override val cache: Cache
) : ApiProvider {
    private var myCommentApi: CommentApi? = null
    private val myMergeRequestApi = GitlabMergeRequestApiCache(
        GitlabMergeRequestApi(infrastructure, credentials), cache
    )

    override val info: ProviderInfo = Gitlab

    override val user: UserApi = GitlabUserApi(infrastructure, credentials)

    override val mergeRequest: MergeRequestApi = myMergeRequestApi

    override val project: ProjectApi = GitlabProjectApi(infrastructure, credentials)

    override val comment: CommentApi
        get() = myCommentApi!!

    override val commit: CommitApi = GitlabCommitApi(infrastructure, credentials)

    override fun setOptions(options: ApiOptions) {
        myMergeRequestApi.options = options
    }

    override fun initialize(currentUser: UserInfo) {
        this.myCommentApi = DraftCommentApi(
            GitlabCommentApi(infrastructure, credentials),
            MemoryDraftCommentStorage(currentUser)
        )
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabClient.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.foundation.Error
import net.ntworld.foundation.Request
import net.ntworld.foundation.Response
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.gitlab.request.GitlabSearchProjectsRequest
import org.gitlab4j.api.Constants
import org.gitlab4j.api.GitLabApi
import org.gitlab4j.api.GitLabApiException
import org.glassfish.jersey.client.ClientProperties.CONNECT_TIMEOUT
import org.glassfish.jersey.client.ClientProperties.READ_TIMEOUT

class GitlabClient {
    companion object {
        private fun makeGitLabApi(credentials: ApiCredentials): GitLabApi {
            val config: HashMap<String, Any> = HashMap()
            config.put(READ_TIMEOUT, 10000)
            config.put(CONNECT_TIMEOUT, 10000)
            val api = GitLabApi(
                credentials.url,
                Constants.TokenType.PRIVATE,
                credentials.token,
                null,
                config
            )
            if (credentials.ignoreSSLCertificateErrors) {
                api.ignoreCertificateErrors = true
            }
            return api
        }

        operator fun <T, R : Response> invoke(
            request: T,
            execute: (GitLabApi.(T) -> R),
            failed: ((Error) -> R)
        ): R where T : Request<R>, T : GitlabRequest {
            return try {
                execute.invoke(
                    makeGitLabApi(request.credentials),
                    request
                )
            } catch (exception: GitLabApiException) {
                failed.invoke(
                    GitlabFailedRequestError(
                        exception.message ?: "Failed request",
                        exception.httpStatus
                    )
                )
            }
        }

        operator fun <T> invoke(
            credentials: ApiCredentials,
            execute: (GitLabApi.() -> T),
            failed: ((Error) -> T)
        ): T {
            return try {
                execute.invoke(
                    makeGitLabApi(credentials)
                )
            } catch (exception: GitLabApiException) {
                failed.invoke(
                    GitlabFailedRequestError(
                        exception.message ?: "Failed request",
                        exception.httpStatus
                    )
                )
            }
        }
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabCommentApi.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.*
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequest.api.CommentApi
import net.ntworld.mergeRequestIntegration.provider.ProviderException
import net.ntworld.mergeRequestIntegration.provider.gitlab.command.*
import net.ntworld.mergeRequestIntegration.provider.gitlab.request.GitlabCreateNoteRequest
import net.ntworld.mergeRequestIntegration.provider.gitlab.request.GitlabGetMRCommentsRequest
import net.ntworld.mergeRequestIntegration.provider.gitlab.request.GitlabGetMRDiscussionsRequest
import net.ntworld.mergeRequestIntegration.provider.gitlab.request.GitlabReplyNoteRequest
import net.ntworld.mergeRequestIntegration.provider.gitlab.transformer.GitlabCommentTransformer
import net.ntworld.mergeRequestIntegration.provider.gitlab.transformer.GitlabDiscussionTransformer
import org.gitlab4j.api.models.Position
import java.util.logging.Level
import java.util.logging.Logger

class GitlabCommentApi(
    private val infrastructure: Infrastructure,
    private val credentials: ApiCredentials
) : CommentApi {

    companion object {
        private val log: Logger = Logger.getLogger("GitlabCommentApi")
    }

    override fun getAll(project: Project, mergeRequestId: String): List<Comment> {
        val request = GitlabGetMRDiscussionsRequest(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt()
        )
        val response = infrastructure.serviceBus() process request ifError {
            throw Exception(it.message)
        }
        val comments = mutableListOf<Comment>()
        response.discussions.forEach {
            comments.addAll(GitlabDiscussionTransformer.transform(it))
        }
        return comments
    }

    private fun getAllGraphQL(project: Project, mergeRequestId: String): List<Comment> {
        val fullPath = findProjectFullPath(project)
        val comments = mutableListOf<Comment>()
        var endCursor = ""
        do {
            val request = GitlabGetMRCommentsRequest(
                credentials = credentials,
                projectFullPath = fullPath,
                mergeRequestInternalId = mergeRequestId.toInt(),
                endCursor = endCursor
            )
            val response = infrastructure.serviceBus() process request ifError {
                throw Exception(it.message)
            }

            val payload = response.payload
            if (null !== payload) {
                endCursor = payload.data.project.mergeRequest.notes.pageInfo.endCursor
                payload.data.project.mergeRequest.notes.nodes.forEach {
                    if (!it.system) {
                        comments.add(GitlabCommentTransformer.transform(it))
                    }
                }
            }
        } while (null === payload || payload.data.project.mergeRequest.notes.pageInfo.hasNextPage)

        return comments
    }

    override fun create(
        project: Project,
        mergeRequestId: String,
        body: String,
        position: CommentPosition?,
        isDraft: Boolean
    ): String? {
        if (isDraft) {
            println(body)
            println(position)
            // TODO: do something
            return ""
        }

        val createdCommentId = if (null === position) {
            createGeneralComment(mergeRequestId, body)
        } else {
            createPositionComment(mergeRequestId, body, position)
        }
        return if (createdCommentId == 0) null else createdCommentId.toString()
    }

    private fun createGeneralComment(mergeRequestId: String, body: String): Int {
        val request = GitlabCreateNoteRequest(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt(),
            body = body,
            position = null
        )
        val response = infrastructure.serviceBus() process request ifError {
            throw ProviderException(it)
        }
        return response.createdCommentId
    }

    private fun createPositionComment(mergeRequestId: String, body: String, commentPosition: CommentPosition) : Int {
        val position = makePosition(commentPosition)
        if (commentPosition.changeType != CommentPositionChangeType.UNKNOWN) {
            if (commentPosition.source == CommentPositionSource.SIDE_BY_SIDE_LEFT) {
                position.newLine = null
                position.newPath = null
            }
            if (commentPosition.source == CommentPositionSource.SIDE_BY_SIDE_RIGHT) {
                position.oldLine = null
                position.oldPath = null
            }
        }
        val request = GitlabCreateNoteRequest(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt(),
            body = body,
            position = position
        )
        val out = infrastructure.serviceBus() process request
        if (out.hasError()) {
            throw ProviderException(out.getResponse().error!!)
        }
        return out.getResponse().createdCommentId
    }

    private fun makePosition(position: CommentPosition): Position {
        val model = Position()
        model.baseSha = position.baseHash
        model.headSha = position.headHash
        model.startSha = position.startHash
        model.oldPath = if (null === position.oldLine || position.oldLine!! < 0) null else position.oldPath
        model.newPath = if (null === position.newLine || position.newLine!! < 0) null else position.newPath
        model.oldLine = if (null === position.oldLine || position.oldLine!! < 0) null else position.oldLine
        model.newLine = if (null === position.newLine || position.newLine!! < 0) null else position.newLine
        model.positionType = Position.PositionType.TEXT
        return model
    }

    override fun reply(project: Project, mergeRequestId: String, repliedComment: Comment, body: String): String? {
        val request = GitlabReplyNoteRequest(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt(),
            discussionId = repliedComment.parentId,
            noteId = repliedComment.id.toInt(),
            body = body
        )

        val response = infrastructure.serviceBus() process request ifError {
            throw ProviderException(it)
        }
        return response.createdCommentId.toString()
    }

    override fun delete(project: Project, mergeRequestId: String, comment: Comment) {
        val command = GitlabDeleteNoteCommand(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt(),
            discussionId = comment.parentId,
            noteId = comment.id.toInt()
        )
        infrastructure.commandBus() process command
    }

    override fun resolve(project: Project, mergeRequestId: String, comment: Comment) {
        infrastructure.commandBus() process GitlabResolveNoteCommand(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt(),
            discussionId = comment.parentId,
            resolve = true
        )
    }

    override fun unresolve(project: Project, mergeRequestId: String, comment: Comment) {
        infrastructure.commandBus() process GitlabResolveNoteCommand(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt(),
            discussionId = comment.parentId,
            resolve = false
        )
    }

    override fun update(project: Project, mergeRequestId: String, comment: Comment, body: String) {
        infrastructure.commandBus() process GitlabUpdateDiffNoteCommand(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt(),
            discussionId = comment.parentId,
            body = body,
            noteId = comment.id.toInt()
        )
    }

    override fun getDraftCount(project: Project, mergeRequestId: String): Int {
        throw Exception("Not implemented in GitlabCommentApi, see DraftCommentApi")
    }

    override fun publishAllDraftComments(project: Project, mergeRequestId: String) {
        throw Exception("Not implemented in GitlabCommentApi, see DraftCommentApi")
    }

    override fun publishDraftComments(project: Project, mergeRequestId: String, commentIds: List<String>) {
        throw Exception("Not implemented in GitlabCommentApi, see DraftCommentApi")
    }

    private fun findProjectFullPath(project: Project): String {
        val url = project.url.replace(credentials.url, "")
        return if (url.startsWith("/")) url.substring(1) else url
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabCommitApi.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.Change
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequest.api.CommitApi
import net.ntworld.mergeRequestIntegration.provider.gitlab.request.GitlabGetCommitChangesRequest
import net.ntworld.mergeRequestIntegration.provider.gitlab.transformer.GitlabDiffTransformer

class GitlabCommitApi(
    private val infrastructure: Infrastructure,
    private val credentials: ApiCredentials
) : CommitApi {

    override fun getChanges(projectId: String, commitId: String): List<Change> {
        val out = infrastructure.serviceBus() process GitlabGetCommitChangesRequest(
            credentials = credentials,
            commitSha = commitId
        )
        return if (out.hasError()) {
            listOf()
        } else {
            out.getResponse().changes.map { GitlabDiffTransformer.transform(it) }
        }
    }

}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabCredentials.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.mergeRequest.api.ApiCredentials

data class GitlabCredentials(
    override val url: String,

    override val token: String,

    override val projectId: String,

    override val login: String = "",

    override val version: String = "v4",

    override val info: String = "",

    override val ignoreSSLCertificateErrors: Boolean = false
): ApiCredentials {

    companion object {
        val Empty = GitlabCredentials("", "", "", "")
    }
}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabFailedRequestError.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.foundation.Error

data class GitlabFailedRequestError(
    override val message: String,
    override val code: Int
): Error {
    override val type: String = "net.ntworld.mergeRequestIntegration.provider.gitlab.GitlabFailedRequestError"
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabFailedRequestException.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.foundation.Error

class GitlabFailedRequestException(error: Error) : Throwable()

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabFuelClient.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import com.github.kittinunf.fuel.core.*
import com.github.kittinunf.result.Result
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import net.ntworld.foundation.Error
import net.ntworld.foundation.Response
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.FuelClient
import net.ntworld.mergeRequestIntegration.provider.gitlab.model.GraphqlRequest
import java.security.cert.X509Certificate
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager

class GitlabFuelClient private constructor(
    private val credentials: ApiCredentials
) : FuelClient(credentials) {
    override fun injectAuthentication(httpRequest: Request): Request {
        return httpRequest.header("PRIVATE-TOKEN", credentials.token)
    }

    val baseUrl: String = when (credentials.version) {
        "v4" -> "${credentials.url}/api/v4"
        else -> throw Exception("Not supported")
    }

    val baseProjectUrl: String = when (credentials.version) {
        "v4" -> "${credentials.url}/api/v4/projects/${credentials.projectId}"
        else -> throw Exception("Not supported")
    }

    fun callGraphQL(graphqlRequest: String): String {
        val httpRequest = makeRequestFactory().post("${credentials.url}/api/graphql")
        httpRequest.header("Authorization", "Bearer ${credentials.token}")
        httpRequest.header("Content-Type", "application/json")
        httpRequest.header("Accept", "application/json")
        httpRequest.body(graphqlRequest)
        val (_, response, result) = httpRequest.responseString()
        return when (result) {
            is Result.Success -> {
                result.value
            }
            is Result.Failure -> {
                throw HttpException(response.statusCode, result.error.message ?: "Unknown")
            }
        }
    }

    fun callGraphQL(query: String, variables: Map<String, String?>): String {
        val graphqlRequest = GraphqlRequest(
            query = query,
            variables = variables
        )
        return this.callGraphQL(json.stringify(GraphqlRequest.serializer(), graphqlRequest))
    }

    companion object {
        operator fun <T, R : Response> invoke(
            request: T,
            execute: (GitlabFuelClient.(T) -> R),
            failed: ((Error) -> R)
        ): R where T : net.ntworld.foundation.Request<R>, T : GitlabRequest {
            return try {
                val client = GitlabFuelClient(request.credentials)
                execute.invoke(client, request)
            } catch (exception: HttpException) {
                failed.invoke(
                    GitlabFailedRequestError(
                        exception.message ?: "Failed request",
                        500
                    )
                )
            }
        }

        operator fun <T> invoke(
            credentials: ApiCredentials,
            execute: (GitlabFuelClient.() -> T),
            failed: ((Error) -> T)
        ) : T {
            return try {
                val client = GitlabFuelClient(credentials)
                execute.invoke(client)
            } catch (exception: HttpException) {
                failed.invoke(
                    GitlabFailedRequestError(
                        exception.message ?: "Failed request",
                        500
                    )
                )
            }
        }
    }

}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabMergeRequestApi.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.*
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequest.api.MergeRequestApi
import net.ntworld.mergeRequest.api.MergeRequestOrdering
import net.ntworld.mergeRequest.query.GetMergeRequestFilter
import net.ntworld.mergeRequestIntegration.internal.MergeRequestSearchResultImpl
import net.ntworld.mergeRequestIntegration.provider.gitlab.command.GitlabApproveMRCommand
import net.ntworld.mergeRequestIntegration.provider.gitlab.command.GitlabUnapproveMRCommand
import net.ntworld.mergeRequestIntegration.provider.gitlab.request.*
import net.ntworld.mergeRequestIntegration.provider.gitlab.transformer.*
import org.gitlab4j.api.Constants

class GitlabMergeRequestApi(
    private val infrastructure: Infrastructure,
    private val credentials: ApiCredentials
) : MergeRequestApi {
    override fun find(projectId: String, mergeRequestId: String): MergeRequest? {
        val out = infrastructure.serviceBus() process GitlabFindMRRequest(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId
        )
        return if (out.hasError()) {
            null
        } else {
            GitlabMRTransformer.transform(out.getResponse().mergeRequest)
        }
    }

    override fun approve(projectId: String, mergeRequestId: String, sha: String) {
        infrastructure.commandBus() process GitlabApproveMRCommand(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt(),
            sha = sha
        )
    }

    override fun unapprove(projectId: String, mergeRequestId: String) {
        infrastructure.commandBus() process GitlabUnapproveMRCommand(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt()
        )
    }

    override fun findApproval(projectId: String, mergeRequestId: String): Approval {
        val out = infrastructure.serviceBus() process GitlabFindMRApprovalRequest(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt()
        )
        return GitlabApprovalTransformer.transform(out.getResponse().approval)
    }

    override fun getPipelines(projectId: String, mergeRequestId: String): List<Pipeline> {
        val out = infrastructure.serviceBus() process GitlabGetMRPipelinesRequest(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt()
        )
        return if (out.hasError()) {
            listOf()
        } else {
            out.getResponse().pipelines.map { GitlabPipelineTransformer.transform(it) }
        }
    }

    override fun getCommits(projectId: String, mergeRequestId: String): List<Commit> {
        val out = infrastructure.serviceBus() process GitlabGetMRCommitsRequest(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt()
        )
        return if (out.hasError()) {
            listOf()
        } else {
            out.getResponse().commits.map { GitlabCommitTransformer.transform(it) }
        }
    }

    override fun getChanges(projectId: String, mergeRequestId: String): List<Change> {
        val out = infrastructure.serviceBus() process GitlabGetMRChangesRequest(
            credentials = credentials,
            mergeRequestInternalId = mergeRequestId.toInt()
        )
        return if (out.hasError()) {
            listOf()
        } else {
            out.getResponse().changes.map { GitlabDiffTransformer.transform(it) }
        }
    }

    override fun search(
        projectId: String,
        currentUserId: String,
        filterBy: GetMergeRequestFilter,
        orderBy: MergeRequestOrdering,
        page: Int,
        itemsPerPage: Int
    ): MergeRequestApi.SearchResult {
        val (order, sort) = resolveOrderAndSort(orderBy)
        val out = infrastructure.serviceBus() process GitlabSearchMRsRequest(
            credentials = credentials,
            state = resolveState(filterBy.state),
            filterById = filterBy.id,
            search = filterBy.search,
            authorId = filterBy.authorId,
            assigneeId = filterBy.assigneeId,
            approverIds = filterBy.approverIds,
            sourceBranch = filterBy.sourceBranch,
            orderBy = order,
            sort = sort,
            page = page,
            perPage = itemsPerPage
        )

        return if (out.hasError()) {
            MergeRequestSearchResultImpl(listOf(), 0, 0, 0)
        } else {
            val response = out.getResponse()
            MergeRequestSearchResultImpl(
                data = response.mergeRequests.map { GitlabMRSimpleTransformer.transform(it) },
                totalItems = response.totalItems,
                totalPages = response.totalPages,
                currentPage = response.currentPage
            )
        }
    }

    override fun findOrFail(projectId: String, mergeRequestId: String): MergeRequest  {
        val mergeRequest = find(projectId, mergeRequestId)
        if (null === mergeRequest) {
            throw Exception("MergeRequest $mergeRequestId not found.")
        }
        return mergeRequest
    }

    private fun resolveState(state: MergeRequestState): Constants.MergeRequestState = when (state) {
        MergeRequestState.ALL -> Constants.MergeRequestState.ALL
        MergeRequestState.OPENED -> Constants.MergeRequestState.OPENED
        MergeRequestState.CLOSED -> Constants.MergeRequestState.CLOSED
        MergeRequestState.MERGED -> Constants.MergeRequestState.MERGED
    }

    private fun resolveOrderAndSort(
        orderBy: MergeRequestOrdering
    ): Pair<Constants.MergeRequestOrderBy, Constants.SortOrder> {
        return when (orderBy) {
            MergeRequestOrdering.RECENTLY_UPDATED -> Pair(
                Constants.MergeRequestOrderBy.UPDATED_AT,
                Constants.SortOrder.DESC
            )
            MergeRequestOrdering.NEWEST -> Pair(Constants.MergeRequestOrderBy.CREATED_AT, Constants.SortOrder.DESC)
            MergeRequestOrdering.OLDEST -> Pair(Constants.MergeRequestOrderBy.CREATED_AT, Constants.SortOrder.ASC)
        }
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabMergeRequestApiCache.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.mergeRequest.MergeRequest
import net.ntworld.mergeRequest.api.*
import net.ntworld.mergeRequest.query.GetMergeRequestFilter
import net.ntworld.mergeRequestIntegration.internal.ApiOptionsImpl
import net.ntworld.mergeRequestIntegration.provider.MergeRequestApiDecorator
import org.joda.time.DateTime

class GitlabMergeRequestApiCache(
    private val api: MergeRequestApi,
    private val cache: Cache
) : MergeRequestApiDecorator(api) {
    var options: ApiOptions = ApiOptionsImpl.DEFAULT

    override fun findOrFail(projectId: String, mergeRequestId: String): MergeRequest {
        if (!options.enableRequestCache) {
            return super.findOrFail(projectId, mergeRequestId)
        }

        val key = makeFindCacheKey(mergeRequestId)
        return cache.getOrRun(key) {
            val mergeRequest = super.findOrFail(projectId, mergeRequestId)

            cache.set(key, mergeRequest)
            mergeRequest
        }
    }

    override fun search(
        projectId: String,
        currentUserId: String,
        filterBy: GetMergeRequestFilter,
        orderBy: MergeRequestOrdering,
        page: Int,
        itemsPerPage: Int
    ): MergeRequestApi.SearchResult {
        val result = super.search(projectId, currentUserId, filterBy, orderBy, page, itemsPerPage)
        if (options.enableRequestCache) {
            result.data.forEach {
                try {
                    val key = makeFindCacheKey(it.id)
                    if (cache.isExpiredAfter(key, DateTime(it.updatedAt))) {
                        cache.remove(key)
                    }
                } catch (cacheNotFound: CacheNotFoundException) {
                }
            }
        }
        return result
    }

    private fun makeFindCacheKey(mergeRequestId: String) = "MR:find:$mergeRequestId"
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabProjectApi.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.Project
import net.ntworld.mergeRequest.UserInfo
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequest.api.ProjectApi
import net.ntworld.mergeRequestIntegration.provider.gitlab.request.GitlabFindProjectRequest
import net.ntworld.mergeRequestIntegration.provider.gitlab.request.GitlabGetProjectMembersRequest
import net.ntworld.mergeRequestIntegration.provider.gitlab.transformer.GitlabMemberTransformer
import net.ntworld.mergeRequestIntegration.provider.gitlab.transformer.GitlabProjectTransformer

class GitlabProjectApi(
    private val infrastructure: Infrastructure,
    private val credentials: ApiCredentials
) : ProjectApi {

    override fun find(projectId: String): Project? {
        val out = infrastructure.serviceBus() process GitlabFindProjectRequest(credentials, projectId.toInt())

        return if (out.hasError()) {
            null
        } else {
            GitlabProjectTransformer.transform(out.getResponse().project)
        }
    }

    override fun getMembers(projectId: String): List<UserInfo> {
        val out = infrastructure.serviceBus() process GitlabGetProjectMembersRequest(credentials)

        return if (out.hasError()) {
            listOf()
        } else {
            out.getResponse().members.map { GitlabMemberTransformer.transform(it) }
        }
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabRequest.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.mergeRequest.api.ApiCredentials

interface GitlabRequest {
    val credentials: ApiCredentials
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabUserApi.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.foundation.Infrastructure
import net.ntworld.mergeRequest.User
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequest.api.UserApi
import net.ntworld.mergeRequestIntegration.provider.ProviderException
import net.ntworld.mergeRequestIntegration.provider.gitlab.request.GitlabFindCurrentUserRequest
import net.ntworld.mergeRequestIntegration.provider.gitlab.transformer.GitlabUserTransformer

class GitlabUserApi(
    private val infrastructure: Infrastructure,
    private val credentials: ApiCredentials
) : UserApi {

    override fun me(): User {
        val response = infrastructure.serviceBus() process GitlabFindCurrentUserRequest(credentials) ifError {
            throw ProviderException(it)
        }
        return GitlabUserTransformer.transform(response.user)
    }
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabUtil.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

import net.ntworld.mergeRequest.UserStatus
import net.ntworld.mergeRequest.api.ApiCredentials

object GitlabUtil {
    fun findUserStatus(state: String): UserStatus {
        return when (state) {
            USER_STATE_ACTIVE -> UserStatus.ACTIVE
            USER_STATE_INACTIVE -> UserStatus.INACTIVE
            else -> UserStatus.INACTIVE
        }
    }

    fun hasMergeApprovalFeature(credentials: ApiCredentials): Boolean {
        return credentials.info.contains(GITLAB_HAS_MERGE_APPROVAL_FEATURE)
    }

    fun getMergeApprovalFeatureInfo() = GITLAB_HAS_MERGE_APPROVAL_FEATURE
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/_const.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab

const val GITLAB_HAS_MERGE_APPROVAL_FEATURE = "mergeApprovalFeature:true"
const val MERGE_REQUEST_STATE_OPENED = "opened"
const val MERGE_REQUEST_STATE_CLOSED = "closed"
const val MERGE_REQUEST_STATE_MERGED = "merged"

const val USER_STATE_ACTIVE = "active"
const val USER_STATE_INACTIVE = "inactive"

const val MERGE_STATUS_CAN_BE_MERGED = "can_be_merged"

const val PIPELINE_STATUS_RUNNING = "running"
const val PIPELINE_STATUS_FAILED = "failed"
const val PIPELINE_STATUS_SUCCESS = "success"
const val PIPELINE_STATUS_PARTIAL_FAILED = "partial_failed"

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabApproveMRCommand.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.command

import net.ntworld.foundation.cqrs.Command
import net.ntworld.mergeRequest.api.ApiCredentials

data class GitlabApproveMRCommand(
    val credentials: ApiCredentials,
    val mergeRequestInternalId: Int,
    val sha: String
) : Command


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabCreateDiffNoteCommand.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.command

import net.ntworld.foundation.cqrs.Command
import net.ntworld.mergeRequest.CommentPosition
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.gitlab.GitlabRequest

data class GitlabCreateDiffNoteCommand(
    val credentials: ApiCredentials,
    val mergeRequestInternalId: Int,
    val body: String,
    val position: CommentPosition
) : Command


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabDeleteNoteCommand.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.command

import net.ntworld.foundation.cqrs.Command
import net.ntworld.mergeRequest.api.ApiCredentials

data class GitlabDeleteNoteCommand(
    val credentials: ApiCredentials,
    val mergeRequestInternalId: Int,
    val discussionId: String,
    val noteId: Int
) : Command


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabResolveNoteCommand.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.command

import net.ntworld.foundation.cqrs.Command
import net.ntworld.mergeRequest.api.ApiCredentials

data class GitlabResolveNoteCommand(
    val credentials: ApiCredentials,
    val mergeRequestInternalId: Int,
    val discussionId: String,
    val resolve: Boolean
) : Command


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabUnapproveMRCommand.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.command

import net.ntworld.foundation.cqrs.Command
import net.ntworld.mergeRequest.api.ApiCredentials

data class GitlabUnapproveMRCommand(
    val credentials: ApiCredentials,
    val mergeRequestInternalId: Int
) : Command


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabUpdateDiffNoteCommand.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.command

import net.ntworld.foundation.cqrs.Command
import net.ntworld.mergeRequest.api.ApiCredentials

class GitlabUpdateDiffNoteCommand(
    val credentials: ApiCredentials,
    val mergeRequestInternalId: Int,
    val discussionId: String,
    val noteId: Int,
    val body: String
): Command

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabApproveMRCommandHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.cqrs.CommandHandler
import net.ntworld.mergeRequestIntegration.provider.gitlab.command.GitlabApproveMRCommand
import org.gitlab4j.api.Constants
import org.gitlab4j.api.GitLabApi

@Handler
class GitlabApproveMRCommandHandler : CommandHandler<GitlabApproveMRCommand> {

    override fun handle(command: GitlabApproveMRCommand) {
        val api = GitLabApi(command.credentials.url, Constants.TokenType.PRIVATE, command.credentials.token)
        api.mergeRequestApi.approveMergeRequest(
            command.credentials.projectId.toInt(),
            command.mergeRequestInternalId,
            command.sha
        )
    }

}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabCreateDiffNoteCommandHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler

import kotlinx.serialization.Serializable
import net.ntworld.foundation.Handler
import net.ntworld.foundation.cqrs.CommandHandler
import net.ntworld.foundation.util.UUIDGenerator
import net.ntworld.mergeRequestIntegration.provider.gitlab.GitlabFuelClient
import net.ntworld.mergeRequestIntegration.provider.gitlab.command.GitlabCreateDiffNoteCommand

@Handler
class GitlabCreateDiffNoteCommandHandler : CommandHandler<GitlabCreateDiffNoteCommand> {

    override fun handle(command: GitlabCreateDiffNoteCommand) = GitlabFuelClient(
        credentials = command.credentials,
        execute = {
            val clientMutationId = generateClientMutationId()
            val graphqlRequest = CustomGraphqlRequest(
                query = mutation,
                variables = CustomVariables(
                    clientMutationId = clientMutationId,
                    body = attachFooterToBody(command.body, clientMutationId),
                    mrIid = "gid://gitlab/MergeRequest/${command.mergeRequestInternalId}",
                    headSha = command.position.headHash,
                    baseSha = command.position.baseHash,
                    startSha = command.position.startHash,
                    oldPath = command.position.oldPath,
                    oldLine = command.position.oldLine,
                    newPath = command.position.newPath,
                    newLine = command.position.newLine
                )
            )
            this.callGraphQL(json.stringify(CustomGraphqlRequest.serializer(), graphqlRequest))
            Unit
        },
        failed = {
            println(it)
            Unit
        }
    )

    private fun generateClientMutationId(): String {
        return "MRI:${UUIDGenerator.generate()}"
    }

    private fun attachFooterToBody(body: String, clientMutationId: String): String {
        return body
    }

    @Serializable
    data class CustomGraphqlRequest(
        val query: String,
        val variables: CustomVariables
    )

    @Serializable
    data class CustomVariables(
        val clientMutationId: String,
        val body: String,
        val mrIid: String,
        val headSha: String,
        val baseSha: String,
        val startSha: String,
        val oldPath: String?,
        val oldLine: Int?,
        val newPath: String?,
        val newLine: Int?
    )

    private val mutation = """
mutation createComment(
  $${"clientMutationId"}: String, 
  $${"body"}: String!, 
  $${"mrIid"}: ID!,
  $${"headSha"}: String!,
  $${"baseSha"}: String,
  $${"startSha"}: String!,
  $${"oldPath"}: String,
  $${"oldLine"}: Int,
  $${"newPath"}: String,
  $${"newLine"}: Int!
) {
  createDiffNote(input: {
    body: $${"body"}
    noteableId: $${"mrIid"},
    clientMutationId: $${"clientMutationId"},
    position: {
      headSha: $${"headSha"},
      baseSha: $${"baseSha"},
      startSha: $${"startSha"},
      paths: {
        oldPath: $${"oldPath"},
        newPath: $${"newPath"}
      },
      oldLine: $${"oldLine"},
      newLine: $${"newLine"}
    }
  }) {
    clientMutationId
  }
}
"""
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabDeleteNoteCommandHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.cqrs.CommandHandler
import net.ntworld.mergeRequestIntegration.provider.gitlab.GitlabFuelClient
import net.ntworld.mergeRequestIntegration.provider.gitlab.command.GitlabDeleteNoteCommand

@Handler
class GitlabDeleteNoteCommandHandler : CommandHandler<GitlabDeleteNoteCommand> {

    override fun handle(command: GitlabDeleteNoteCommand) = GitlabFuelClient(
        credentials = command.credentials,
        execute = {
            this.deleteJson("$baseProjectUrl/merge_requests/${command.mergeRequestInternalId}/discussions/${command.discussionId}/notes/${command.noteId}")
            Unit
        },
        failed = {}
    )

}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabResolveNoteCommandHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.cqrs.CommandHandler
import net.ntworld.mergeRequestIntegration.provider.gitlab.GitlabFuelClient
import net.ntworld.mergeRequestIntegration.provider.gitlab.command.GitlabResolveNoteCommand

@Handler
class GitlabResolveNoteCommandHandler : CommandHandler<GitlabResolveNoteCommand> {

    override fun handle(command: GitlabResolveNoteCommand) = GitlabFuelClient(
        credentials = command.credentials,
        execute = {
            val params = listOf(
                Pair("resolved", command.resolve)
            )
            this.putJson(
                "$baseProjectUrl/merge_requests/${command.mergeRequestInternalId}/discussions/${command.discussionId}",
                params
            )
            Unit
        },
        failed = {
            println(it)
        }
    )

}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabUnapproveMRCommandHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.cqrs.CommandHandler
import net.ntworld.mergeRequestIntegration.provider.gitlab.command.GitlabUnapproveMRCommand
import org.gitlab4j.api.Constants
import org.gitlab4j.api.GitLabApi

@Handler
class GitlabUnapproveMRCommandHandler: CommandHandler<GitlabUnapproveMRCommand> {

    override fun handle(command: GitlabUnapproveMRCommand) {
        val api = GitLabApi(command.credentials.url, Constants.TokenType.PRIVATE, command.credentials.token)
        api.mergeRequestApi.unapproveMergeRequest(
            command.credentials.projectId.toInt(),
            command.mergeRequestInternalId
        )
    }

}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabUpdateDiffNoteCommandHandler.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler

import net.ntworld.foundation.Handler
import net.ntworld.foundation.cqrs.CommandHandler
import net.ntworld.mergeRequestIntegration.provider.gitlab.GitlabFuelClient
import net.ntworld.mergeRequestIntegration.provider.gitlab.command.GitlabUpdateDiffNoteCommand

@Handler
class GitlabUpdateDiffNoteCommandHandler : CommandHandler<GitlabUpdateDiffNoteCommand> {
    override fun handle(command: GitlabUpdateDiffNoteCommand) = GitlabFuelClient(
        credentials = command.credentials,
        execute = {
            val params = listOf(
                Pair("body", command.body)
            )
            this.putJson(
                "$baseProjectUrl/merge_requests/${command.mergeRequestInternalId}/discussions/${command.discussionId}/notes/${command.noteId}",
                params
            )
            Unit
        },
        failed = {
            println(it)
        }
    )
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/ApprovalModel.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.model

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ApprovalModel(
    val approved: Boolean,

    @SerialName("approvals_required")
    val approvalsRequired: Int,

    @SerialName("approvals_left")
    val approvalsLeft: Int,

    @SerialName("suggested_approvers")
    val suggestedApprovers: List<UserInfoModel>,

    val approvers: List<ApproverModel>,

    @SerialName("approved_by")
    val approvedBy: List<ApproverModel>,

    @SerialName("user_has_approved")
    val hasApproved: Boolean,

    @SerialName("user_can_approve")
    val canApprove: Boolean
) {
    companion object {
        val Empty = ApprovalModel(
            approved = false,
            approvalsRequired = 0,
            approvalsLeft = 0,
            suggestedApprovers = listOf(),
            approvers = listOf(),
            approvedBy = listOf(),
            hasApproved = false,
            canApprove = false
        )
    }
}


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/ApproverModel.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.model

import kotlinx.serialization.Serializable

@Serializable
data class ApproverModel(
    val user: UserInfoModel
)

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/GetCommentsPayload.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.model

import kotlinx.serialization.Serializable

@Serializable
data class GetCommentsPayload(
    val data: Data
) {
    @Serializable
    data class Data(
        val project: Project
    )

    @Serializable
    data class Project(
        val id: String,
        val name: String,
        val mergeRequest: MergeRequest
    )

    @Serializable
    data class MergeRequest(
        val id: String,
        val iid: String,
        val notes: NoteCollection
    )

    @Serializable
    data class NoteCollection(
        val pageInfo: NotesPageInfo,
        val nodes: List<Note>
    )

    @Serializable
    data class NotesPageInfo(
        val endCursor: String,
        val startCursor: String,
        val hasNextPage: Boolean,
        val hasPreviousPage: Boolean
    )

    @Serializable
    data class Note(
        val id: String,
        val body: String,
        val bodyHtml: String,
        val author: User,
        val resolvable: Boolean,
        val resolvedAt: String?,
        val resolvedBy: User?,
        val system: Boolean,
        val createdAt: String,
        val updatedAt: String,
        val position: NotePosition?
    )

    @Serializable
    data class User(
        val name: String,
        val username: String,
        val webUrl: String,
        val avatarUrl: String
    )

    @Serializable
    data class NotePosition(
        val diffRefs: DiffRef,
        val filePath: String,
        val newLine: Int?,
        val oldLine: Int?,
        val newPath: String?,
        val oldPath: String?,
        val positionType: String
    )

    @Serializable
    data class DiffRef(
        val baseSha: String,
        val startSha: String,
        val headSha: String
    )
}

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/GraphqlRequest.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.model

import kotlinx.serialization.Serializable

@Serializable
data class GraphqlRequest(
    val query: String,
    val variables: Map<String, String?>
)


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/PipelineModel.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.model

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class PipelineModel(
    val id: Int,

    val sha: String,

    val ref: String,

    val status: String,

    @SerialName("created_at")
    val createdAt: String? = null,

    @SerialName("updated_at")
    val updatedAt: String? = null,

    @SerialName("web_url")
    val webUrl: String
)

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/ReplyCommentPayload.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.model

import kotlinx.serialization.Serializable

@Serializable
data class ReplyCommentPayload(
    val id: Int
)


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/UserInfoModel.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.model

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class UserInfoModel(
    val id: Int,

    val name: String,

    val username: String,

    @SerialName("avatar_url")
    val avatarUrl: String,

    @SerialName("web_url")
    val webUrl: String,

    val state: String
)

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabCreateNoteRequest.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.gitlab.GitlabRequest
import net.ntworld.mergeRequestIntegration.provider.gitlab.response.GitlabCreateNoteResponse
import org.gitlab4j.api.models.Position

data class GitlabCreateNoteRequest(
    override val credentials: ApiCredentials,
    val mergeRequestInternalId: Int,
    val position: Position?,
    val body: String
) : GitlabRequest, Request<GitlabCreateNoteResponse>


================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabFindCurrentUserRequest.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.gitlab.GitlabRequest
import net.ntworld.mergeRequestIntegration.provider.gitlab.response.GitlabFindUserResponse

data class GitlabFindCurrentUserRequest(
    override val credentials: ApiCredentials
): GitlabRequest, Request<GitlabFindUserResponse>

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabFindMRApprovalRequest.kt
================================================
package net.ntworld.mergeRequestIntegration.provider.gitlab.request

import net.ntworld.foundation.Request
import net.ntworld.mergeRequest.api.ApiCredentials
import net.ntworld.mergeRequestIntegration.provider.gitlab.GitlabRequest
import net.ntworld.mergeRequestIntegration.provider.gitlab.response.GitlabFindMRApprovalResponse

data class GitlabFindMRApprovalRequest(
    override val credentials: ApiCredentials,
    val mergeRequestInternalId: Int
) : GitlabRequest, Request<GitlabFindMRApprovalResponse>

================================================
FILE: merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab
Download .txt
gitextract_0m11ptlg/

├── .gitignore
├── LICENSE
├── README.md
├── build.gradle.kts
├── contracts/
│   ├── build.gradle.kts
│   ├── settings.gradle.kts
│   └── src/
│       └── main/
│           └── kotlin/
│               └── net/
│                   └── ntworld/
│                       └── mergeRequest/
│                           ├── Approval.kt
│                           ├── Change.kt
│                           ├── Comment.kt
│                           ├── CommentPosition.kt
│                           ├── CommentPositionChangeType.kt
│                           ├── CommentPositionSource.kt
│                           ├── Commit.kt
│                           ├── DateTime.kt
│                           ├── DateTimeSerializer.kt
│                           ├── DiffReference.kt
│                           ├── MergeRequest.kt
│                           ├── MergeRequestInfo.kt
│                           ├── MergeRequestState.kt
│                           ├── Pipeline.kt
│                           ├── PipelineStatus.kt
│                           ├── Project.kt
│                           ├── ProjectVisibility.kt
│                           ├── ProviderData.kt
│                           ├── ProviderInfo.kt
│                           ├── ProviderStatus.kt
│                           ├── User.kt
│                           ├── UserInfo.kt
│                           ├── UserStatus.kt
│                           ├── api/
│                           │   ├── ApiConnection.kt
│                           │   ├── ApiCredentials.kt
│                           │   ├── ApiOptions.kt
│                           │   ├── ApiProvider.kt
│                           │   ├── Cache.kt
│                           │   ├── CacheNotFoundException.kt
│                           │   ├── CommentApi.kt
│                           │   ├── CommitApi.kt
│                           │   ├── DraftCommentStorage.kt
│                           │   ├── MergeRequestApi.kt
│                           │   ├── MergeRequestOrdering.kt
│                           │   ├── ProjectApi.kt
│                           │   └── UserApi.kt
│                           ├── command/
│                           │   ├── ApproveMergeRequestCommand.kt
│                           │   ├── DeleteCommentCommand.kt
│                           │   ├── ResolveCommentCommand.kt
│                           │   ├── UnapproveMergeRequestCommand.kt
│                           │   └── UnresolveCommentCommand.kt
│                           ├── query/
│                           │   ├── FindApprovalQuery.kt
│                           │   ├── FindApprovalQueryResult.kt
│                           │   ├── FindMergeRequestQuery.kt
│                           │   ├── FindMergeRequestQueryResult.kt
│                           │   ├── GetCommentsQuery.kt
│                           │   ├── GetCommentsQueryResult.kt
│                           │   ├── GetCommitsQuery.kt
│                           │   ├── GetCommitsQueryResult.kt
│                           │   ├── GetMergeRequestFilter.kt
│                           │   ├── GetMergeRequestsQuery.kt
│                           │   ├── GetMergeRequestsQueryResult.kt
│                           │   ├── GetPipelinesQuery.kt
│                           │   ├── GetPipelinesQueryResult.kt
│                           │   ├── GetProjectMembersQuery.kt
│                           │   ├── GetProjectMembersQueryResult.kt
│                           │   └── QueryBase.kt
│                           ├── request/
│                           │   ├── CreateCommentRequest.kt
│                           │   ├── PublishAllCommentsRequest.kt
│                           │   ├── PublishCommentsRequest.kt
│                           │   ├── ReplyCommentRequest.kt
│                           │   └── UpdateCommentRequest.kt
│                           └── response/
│                               ├── CreateCommentResponse.kt
│                               ├── PublishAllCommentsResponse.kt
│                               ├── PublishCommentsResponse.kt
│                               ├── ReplyCommentResponse.kt
│                               └── UpdateCommentResponse.kt
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── merge-request-integration/
│   ├── build.gradle.kts
│   ├── settings.gradle.kts
│   └── src/
│       ├── main/
│       │   └── kotlin/
│       │       └── net/
│       │           └── ntworld/
│       │               └── mergeRequestIntegration/
│       │                   ├── DefaultProviderStorage.kt
│       │                   ├── MergeRequestIntegrationInfrastructure.kt
│       │                   ├── ProviderStorage.kt
│       │                   ├── _const.kt
│       │                   ├── commandHandler/
│       │                   │   ├── ApproveMergeRequestCommandHandler.kt
│       │                   │   ├── DeleteCommentCommandHandler.kt
│       │                   │   ├── ResolveCommentCommandHandler.kt
│       │                   │   ├── UnapproveMergeRequestCommandHandler.kt
│       │                   │   └── UnresolveCommentCommandHandler.kt
│       │                   ├── exception/
│       │                   │   ├── InvalidCacheKeyException.kt
│       │                   │   ├── InvalidTTLException.kt
│       │                   │   └── ProviderNotFoundException.kt
│       │                   ├── internal/
│       │                   │   ├── ApiOptionsImpl.kt
│       │                   │   ├── ApprovalImpl.kt
│       │                   │   ├── ChangeImpl.kt
│       │                   │   ├── CommentImpl.kt
│       │                   │   ├── CommentPositionImpl.kt
│       │                   │   ├── CommitImpl.kt
│       │                   │   ├── DiffReferenceImpl.kt
│       │                   │   ├── MergeRequestImpl.kt
│       │                   │   ├── MergeRequestInfoImpl.kt
│       │                   │   ├── MergeRequestSearchResultImpl.kt
│       │                   │   ├── PipelineImpl.kt
│       │                   │   ├── ProjectImpl.kt
│       │                   │   ├── ProviderDataImpl.kt
│       │                   │   ├── UserImpl.kt
│       │                   │   └── UserInfoImpl.kt
│       │                   ├── provider/
│       │                   │   ├── DraftCommentApi.kt
│       │                   │   ├── FuelClient.kt
│       │                   │   ├── MemoryCache.kt
│       │                   │   ├── MemoryDraftCommentStorage.kt
│       │                   │   ├── MergeRequestApiDecorator.kt
│       │                   │   ├── ProviderException.kt
│       │                   │   ├── Transformer.kt
│       │                   │   ├── github/
│       │                   │   │   ├── Github.kt
│       │                   │   │   ├── GithubApiProvider.kt
│       │                   │   │   ├── GithubClient.kt
│       │                   │   │   ├── GithubFailedRequestError.kt
│       │                   │   │   ├── GithubFuelClient.kt
│       │                   │   │   ├── GithubMergeRequestApi.kt
│       │                   │   │   ├── GithubProjectApi.kt
│       │                   │   │   ├── GithubRequest.kt
│       │                   │   │   ├── GithubUserApi.kt
│       │                   │   │   ├── GithubUtil.kt
│       │                   │   │   ├── _const.kt
│       │                   │   │   ├── model/
│       │                   │   │   │   ├── PullRequestSearchItem.kt
│       │                   │   │   │   └── SearchPullRequestResult.kt
│       │                   │   │   ├── request/
│       │                   │   │   │   ├── GithubFindCurrentUserRequest.kt
│       │                   │   │   │   ├── GithubFindRepositoryRequest.kt
│       │                   │   │   │   ├── GithubSearchPRsRequest.kt
│       │                   │   │   │   └── GithubSearchRepositoriesRequest.kt
│       │                   │   │   ├── requestHandler/
│       │                   │   │   │   ├── GithubFindCurrentUserRequestHandler.kt
│       │                   │   │   │   ├── GithubFindRepositoryRequestHandler.kt
│       │                   │   │   │   ├── GithubSearchPRsRequestHandler.kt
│       │                   │   │   │   └── GithubSearchRepositoriesRequestHandler.kt
│       │                   │   │   ├── response/
│       │                   │   │   │   ├── GithubFindRepositoryResponse.kt
│       │                   │   │   │   ├── GithubFindUserResponse.kt
│       │                   │   │   │   ├── GithubSearchPRsResponse.kt
│       │                   │   │   │   └── GithubSearchRepositoriesResponse.kt
│       │                   │   │   ├── transformer/
│       │                   │   │   │   ├── GithubRepositoryTransformer.kt
│       │                   │   │   │   ├── GithubSearchPullRequestItemTransformer.kt
│       │                   │   │   │   └── GithubUserTransformer.kt
│       │                   │   │   └── vo/
│       │                   │   │       ├── GithubMergeRequestId.kt
│       │                   │   │       ├── GithubProjectId.kt
│       │                   │   │       └── GithubUserId.kt
│       │                   │   └── gitlab/
│       │                   │       ├── Gitlab.kt
│       │                   │       ├── GitlabApiProvider.kt
│       │                   │       ├── GitlabClient.kt
│       │                   │       ├── GitlabCommentApi.kt
│       │                   │       ├── GitlabCommitApi.kt
│       │                   │       ├── GitlabCredentials.kt
│       │                   │       ├── GitlabFailedRequestError.kt
│       │                   │       ├── GitlabFailedRequestException.kt
│       │                   │       ├── GitlabFuelClient.kt
│       │                   │       ├── GitlabMergeRequestApi.kt
│       │                   │       ├── GitlabMergeRequestApiCache.kt
│       │                   │       ├── GitlabProjectApi.kt
│       │                   │       ├── GitlabRequest.kt
│       │                   │       ├── GitlabUserApi.kt
│       │                   │       ├── GitlabUtil.kt
│       │                   │       ├── _const.kt
│       │                   │       ├── command/
│       │                   │       │   ├── GitlabApproveMRCommand.kt
│       │                   │       │   ├── GitlabCreateDiffNoteCommand.kt
│       │                   │       │   ├── GitlabDeleteNoteCommand.kt
│       │                   │       │   ├── GitlabResolveNoteCommand.kt
│       │                   │       │   ├── GitlabUnapproveMRCommand.kt
│       │                   │       │   └── GitlabUpdateDiffNoteCommand.kt
│       │                   │       ├── commandHandler/
│       │                   │       │   ├── GitlabApproveMRCommandHandler.kt
│       │                   │       │   ├── GitlabCreateDiffNoteCommandHandler.kt
│       │                   │       │   ├── GitlabDeleteNoteCommandHandler.kt
│       │                   │       │   ├── GitlabResolveNoteCommandHandler.kt
│       │                   │       │   ├── GitlabUnapproveMRCommandHandler.kt
│       │                   │       │   └── GitlabUpdateDiffNoteCommandHandler.kt
│       │                   │       ├── model/
│       │                   │       │   ├── ApprovalModel.kt
│       │                   │       │   ├── ApproverModel.kt
│       │                   │       │   ├── GetCommentsPayload.kt
│       │                   │       │   ├── GraphqlRequest.kt
│       │                   │       │   ├── PipelineModel.kt
│       │                   │       │   ├── ReplyCommentPayload.kt
│       │                   │       │   └── UserInfoModel.kt
│       │                   │       ├── request/
│       │                   │       │   ├── GitlabCreateNoteRequest.kt
│       │                   │       │   ├── GitlabFindCurrentUserRequest.kt
│       │                   │       │   ├── GitlabFindMRApprovalRequest.kt
│       │                   │       │   ├── GitlabFindMRRequest.kt
│       │                   │       │   ├── GitlabFindProjectRequest.kt
│       │                   │       │   ├── GitlabFindUserRequest.kt
│       │                   │       │   ├── GitlabGetCommitChangesRequest.kt
│       │                   │       │   ├── GitlabGetMRChangesRequest.kt
│       │                   │       │   ├── GitlabGetMRCommentsRequest.kt
│       │                   │       │   ├── GitlabGetMRCommitsRequest.kt
│       │                   │       │   ├── GitlabGetMRDiscussionsRequest.kt
│       │                   │       │   ├── GitlabGetMRPipelinesRequest.kt
│       │                   │       │   ├── GitlabGetProjectMembersRequest.kt
│       │                   │       │   ├── GitlabReplyNoteRequest.kt
│       │                   │       │   ├── GitlabSearchMRsRequest.kt
│       │                   │       │   └── GitlabSearchProjectsRequest.kt
│       │                   │       ├── requestHandler/
│       │                   │       │   ├── GitlabCreateNoteRequestHandler.kt
│       │                   │       │   ├── GitlabFindCurrentUserRequestHandler.kt
│       │                   │       │   ├── GitlabFindMRApprovalRequestHandler.kt
│       │                   │       │   ├── GitlabFindMRRequestHandler.kt
│       │                   │       │   ├── GitlabFindProjectRequestHandler.kt
│       │                   │       │   ├── GitlabFindUserRequestHandler.kt
│       │                   │       │   ├── GitlabGetCommitChangesRequestHandler.kt
│       │                   │       │   ├── GitlabGetMRChangesRequestHandler.kt
│       │                   │       │   ├── GitlabGetMRCommentsRequestHandler.kt
│       │                   │       │   ├── GitlabGetMRCommitsRequestHandler.kt
│       │                   │       │   ├── GitlabGetMRDiscussionsRequestHandler.kt
│       │                   │       │   ├── GitlabGetMRPipelinesRequestHandler.kt
│       │                   │       │   ├── GitlabGetProjectMembersRequestHandler.kt
│       │                   │       │   ├── GitlabReplyNoteRequestHandler.kt
│       │                   │       │   ├── GitlabSearchMRsRequestHandler.kt
│       │                   │       │   └── GitlabSearchProjectsRequestHandler.kt
│       │                   │       ├── response/
│       │                   │       │   ├── GitlabCreateNoteResponse.kt
│       │                   │       │   ├── GitlabFindMRApprovalResponse.kt
│       │                   │       │   ├── GitlabFindMRResponse.kt
│       │                   │       │   ├── GitlabFindProjectResponse.kt
│       │                   │       │   ├── GitlabFindUserResponse.kt
│       │                   │       │   ├── GitlabGetCommitChangesResponse.kt
│       │                   │       │   ├── GitlabGetMRChangesResponse.kt
│       │                   │       │   ├── GitlabGetMRCommentsResponse.kt
│       │                   │       │   ├── GitlabGetMRCommitsResponse.kt
│       │                   │       │   ├── GitlabGetMRDiscussionsResponse.kt
│       │                   │       │   ├── GitlabGetMRPipelinesResponse.kt
│       │                   │       │   ├── GitlabGetProjectMembersResponse.kt
│       │                   │       │   ├── GitlabReplyNoteResponse.kt
│       │                   │       │   ├── GitlabSearchMRsResponse.kt
│       │                   │       │   └── GitlabSearchProjectsResponse.kt
│       │                   │       └── transformer/
│       │                   │           ├── GitlabApprovalTransformer.kt
│       │                   │           ├── GitlabCommentTransformer.kt
│       │                   │           ├── GitlabCommitTransformer.kt
│       │                   │           ├── GitlabDiffRefTransformer.kt
│       │                   │           ├── GitlabDiffTransformer.kt
│       │                   │           ├── GitlabDiscussionTransformer.kt
│       │                   │           ├── GitlabMRSimpleTransformer.kt
│       │                   │           ├── GitlabMRTransformer.kt
│       │                   │           ├── GitlabMemberTransformer.kt
│       │                   │           ├── GitlabPipelineTransformer.kt
│       │                   │           ├── GitlabProjectTransformer.kt
│       │                   │           ├── GitlabUserInfoTransformer.kt
│       │                   │           └── GitlabUserTransformer.kt
│       │                   ├── queryHandler/
│       │                   │   ├── FindApprovalQueryHandler.kt
│       │                   │   ├── FindMergeRequestQueryHandler.kt
│       │                   │   ├── GetCommentsQueryHandler.kt
│       │                   │   ├── GetCommitsQueryHandler.kt
│       │                   │   ├── GetMergeRequestsQueryHandler.kt
│       │                   │   ├── GetPipelinesQueryHandler.kt
│       │                   │   └── GetProjectMembersQueryHandler.kt
│       │                   ├── requestHandler/
│       │                   │   ├── CreateCommentRequestHandler.kt
│       │                   │   ├── PublishAllCommentsRequestHandler.kt
│       │                   │   ├── PublishCommentsRequestHandler.kt
│       │                   │   ├── ReplyCommentRequestHandler.kt
│       │                   │   └── UpdateCommentRequestHandler.kt
│       │                   ├── update/
│       │                   │   ├── UpdateManager.kt
│       │                   │   └── UpdateMetadata.kt
│       │                   └── util/
│       │                       ├── DateTimeUtil.kt
│       │                       └── SavedFiltersUtil.kt
│       └── test/
│           ├── kotlin/
│           │   └── net/
│           │       └── ntworld/
│           │           └── mergeRequestIntegration/
│           │               └── provider/
│           │                   └── MemoryCacheTests.kt
│           └── resources/
│               └── update-metadata.json
├── merge-request-integration-ce/
│   ├── build.gradle.kts
│   ├── doc/
│   │   ├── description.html
│   │   ├── release-notes.2019.3.0.html
│   │   ├── release-notes.2019.3.1.html
│   │   ├── release-notes.2019.3.2.html
│   │   ├── release-notes.2019.3.3.html
│   │   ├── release-notes.2019.3.4.html
│   │   ├── release-notes.2019.3.5.html
│   │   ├── release-notes.2020.1.0.html
│   │   ├── release-notes.2020.1.1.html
│   │   ├── release-notes.2020.1.2.html
│   │   ├── release-notes.2020.1.3.html
│   │   ├── release-notes.2020.1.4.html
│   │   ├── release-notes.2020.1.5.html
│   │   ├── release-notes.2020.2.0.html
│   │   └── release-notes.2020.3.0.html
│   ├── settings.gradle.kts
│   └── src/
│       └── main/
│           ├── kotlin/
│           │   └── net/
│           │       └── ntworld/
│           │           └── mergeRequestIntegrationIdeCE/
│           │               ├── CommunityApplicationServiceProvider.kt
│           │               ├── CommunityProjectServiceProvider.kt
│           │               ├── Configuration.kt
│           │               ├── DiffExtension.kt
│           │               ├── DiffViewAddCommentAction.kt
│           │               ├── DiffViewToggleCommentsAction.kt
│           │               ├── GithubConnectionsConfigurable.kt
│           │               ├── GitlabConnectionsConfigurable.kt
│           │               ├── MainToolWindowFactory.kt
│           │               └── SingleMRToolWindowFactory.kt
│           └── resources/
│               └── META-INF/
│                   └── plugin.xml
├── merge-request-integration-core/
│   ├── build.gradle.kts
│   ├── settings.gradle.kts
│   └── src/
│       ├── main/
│       │   ├── kotlin/
│       │   │   └── net/
│       │   │       └── ntworld/
│       │   │           └── mergeRequestIntegrationIde/
│       │   │               ├── AbstractModel.kt
│       │   │               ├── AbstractPresenter.kt
│       │   │               ├── AbstractSimpleModel.kt
│       │   │               ├── AbstractSimplePresenter.kt
│       │   │               ├── AbstractSimpleView.kt
│       │   │               ├── AbstractView.kt
│       │   │               ├── Component.kt
│       │   │               ├── ComponentFactory.kt
│       │   │               ├── DataChangedSource.kt
│       │   │               ├── DefaultComponentFactory.kt
│       │   │               ├── IdeInfrastructure.kt
│       │   │               ├── Model.kt
│       │   │               ├── Presenter.kt
│       │   │               ├── SimpleModel.kt
│       │   │               ├── SimplePresenter.kt
│       │   │               ├── SimpleView.kt
│       │   │               ├── View.kt
│       │   │               ├── _const.kt
│       │   │               ├── compatibility/
│       │   │               │   ├── IntellijIdeApi.kt
│       │   │               │   ├── Version193Adapter.kt
│       │   │               │   ├── Version201Adapter.kt
│       │   │               │   └── Version203Adapter.kt
│       │   │               ├── component/
│       │   │               │   ├── Icons.kt
│       │   │               │   ├── PaginationToolbar.kt
│       │   │               │   ├── PaginationToolbarImpl.kt
│       │   │               │   ├── comment/
│       │   │               │   │   ├── CommentComponent.kt
│       │   │               │   │   ├── CommentComponentFactory.kt
│       │   │               │   │   ├── CommentComponentFactoryImpl.kt
│       │   │               │   │   ├── CommentComponentImpl.kt
│       │   │               │   │   ├── CommentEvent.kt
│       │   │               │   │   ├── CommentEventPropagator.kt
│       │   │               │   │   ├── EditorComponent.kt
│       │   │               │   │   ├── EditorComponentImpl.kt
│       │   │               │   │   ├── GroupComponent.kt
│       │   │               │   │   ├── GroupComponentImpl.kt
│       │   │               │   │   └── Options.kt
│       │   │               │   ├── dialog/
│       │   │               │   │   ├── LegalWarningDialog.form
│       │   │               │   │   └── LegalWarningDialog.kt
│       │   │               │   ├── gutter/
│       │   │               │   │   ├── GutterActionType.kt
│       │   │               │   │   ├── GutterIconRenderer.kt
│       │   │               │   │   ├── GutterIconRendererActionListener.kt
│       │   │               │   │   ├── GutterIconRendererFactory.kt
│       │   │               │   │   ├── GutterIconRendererImpl.kt
│       │   │               │   │   ├── GutterPosition.kt
│       │   │               │   │   └── GutterState.kt
│       │   │               │   └── thread/
│       │   │               │       ├── ThreadFactory.kt
│       │   │               │       ├── ThreadModel.kt
│       │   │               │       ├── ThreadModelImpl.kt
│       │   │               │       ├── ThreadPresenter.kt
│       │   │               │       ├── ThreadPresenterImpl.kt
│       │   │               │       ├── ThreadView.kt
│       │   │               │       └── ThreadViewImpl.kt
│       │   │               ├── configuration/
│       │   │               │   ├── README.md
│       │   │               │   └── vos/
│       │   │               │       └── GitRemotePathInfo.kt
│       │   │               ├── diff/
│       │   │               │   ├── AbstractDiffView.kt
│       │   │               │   ├── CommentPoint.kt
│       │   │               │   ├── DiffExtensionBase.kt
│       │   │               │   ├── DiffFactory.kt
│       │   │               │   ├── DiffModel.kt
│       │   │               │   ├── DiffModelImpl.kt
│       │   │               │   ├── DiffPresenter.kt
│       │   │               │   ├── DiffPresenterImpl.kt
│       │   │               │   ├── DiffView.kt
│       │   │               │   ├── DiffViewAddCommentActionBase.kt
│       │   │               │   ├── DiffViewToggleCommentsActionBase.kt
│       │   │               │   ├── SimpleOneSideDiffView.kt
│       │   │               │   ├── TwoSideTextDiffView.kt
│       │   │               │   └── UnifiedDiffView.kt
│       │   │               ├── exception/
│       │   │               │   └── InvalidConnectionException.kt
│       │   │               ├── infrastructure/
│       │   │               │   ├── AbstractApplicationServiceProvider.kt
│       │   │               │   ├── AbstractProjectServiceProvider.kt
│       │   │               │   ├── ApplicationServiceProvider.kt
│       │   │               │   ├── ProjectServiceProvider.kt
│       │   │               │   ├── ProviderSettings.kt
│       │   │               │   ├── ReviewContext.kt
│       │   │               │   ├── ReviewContextManager.kt
│       │   │               │   ├── internal/
│       │   │               │   │   ├── ApiCredentialsImpl.kt
│       │   │               │   │   ├── DiffPreviewProviderImpl.kt
│       │   │               │   │   ├── DiffRequestProcessorImpl.kt
│       │   │               │   │   ├── ProviderSettingsImpl.kt
│       │   │               │   │   ├── ReviewContextImpl.kt
│       │   │               │   │   ├── ReviewContextManagerImpl.kt
│       │   │               │   │   └── ServiceBase.kt
│       │   │               │   ├── notifier/
│       │   │               │   │   ├── DiffNotifier.kt
│       │   │               │   │   ├── MergeRequestDataNotifier.kt
│       │   │               │   │   ├── ProjectNotifier.kt
│       │   │               │   │   ├── ProjectNotifierAdapter.kt
│       │   │               │   │   ├── ReworkEditorNotifier.kt
│       │   │               │   │   ├── ReworkWatcherNotifier.kt
│       │   │               │   │   ├── SingleMRToolWindowNotifier.kt
│       │   │               │   │   └── provider/
│       │   │               │   │       └── MergeRequestDataProvider.kt
│       │   │               │   ├── service/
│       │   │               │   │   ├── FiltersStorageService.kt
│       │   │               │   │   ├── RepositoryFileService.kt
│       │   │               │   │   ├── internal/
│       │   │               │   │   │   └── FiltersStorageServiceImpl.kt
│       │   │               │   │   └── repositoryFile/
│       │   │               │   │       ├── CachedRepositoryFile.kt
│       │   │               │   │       ├── LocalRepositoryFileService.kt
│       │   │               │   │       └── RepositoryFileDecorator.kt
│       │   │               │   └── setting/
│       │   │               │       ├── ApplicationSettings.kt
│       │   │               │       ├── ApplicationSettingsImpl.kt
│       │   │               │       ├── ApplicationSettingsManager.kt
│       │   │               │       ├── ApplicationSettingsManagerImpl.kt
│       │   │               │       └── option/
│       │   │               │           ├── BooleanOption.kt
│       │   │               │           ├── CheckoutTargetBranchOption.kt
│       │   │               │           ├── DisplayCommentsInDiffViewOption.kt
│       │   │               │           ├── DisplayMergeRequestStateOption.kt
│       │   │               │           ├── DisplayUpVotesAndDownVotesOption.kt
│       │   │               │           ├── EnableRequestCacheOption.kt
│       │   │               │           ├── EnableReworkProcessOption.kt
│       │   │               │           ├── MaxDiffChangesOpenedAutomaticallyOption.kt
│       │   │               │           ├── SaveMRFilterStateOption.kt
│       │   │               │           ├── SettingOption.kt
│       │   │               │           └── ShowAddCommentIconsInDiffViewGutterOption.kt
│       │   │               ├── mergeRequest/
│       │   │               │   ├── comments/
│       │   │               │   │   ├── CommentsTabFactory.kt
│       │   │               │   │   ├── CommentsTabModel.kt
│       │   │               │   │   ├── CommentsTabModelImpl.kt
│       │   │               │   │   ├── CommentsTabPresenter.kt
│       │   │               │   │   ├── CommentsTabPresenterImpl.kt
│       │   │               │   │   ├── CommentsTabView.kt
│       │   │               │   │   ├── CommentsTabViewImpl.kt
│       │   │               │   │   └── tree/
│       │   │               │   │       ├── CommentTreeFactory.kt
│       │   │               │   │       ├── CommentTreeModel.kt
│       │   │               │   │       ├── CommentTreeModelImpl.kt
│       │   │               │   │       ├── CommentTreePresenter.kt
│       │   │               │   │       ├── CommentTreePresenterImpl.kt
│       │   │               │   │       ├── CommentTreeView.kt
│       │   │               │   │       ├── CommentTreeViewImpl.kt
│       │   │               │   │       ├── CommentTreeViewToolbar.kt
│       │   │               │   │       └── node/
│       │   │               │   │           ├── AbstractNode.kt
│       │   │               │   │           ├── CommentNode.kt
│       │   │               │   │           ├── FileLineNode.kt
│       │   │               │   │           ├── FileNode.kt
│       │   │               │   │           ├── GeneralCommentsNode.kt
│       │   │               │   │           ├── Node.kt
│       │   │               │   │           ├── NodeDescriptorService.kt
│       │   │               │   │           ├── NodeDescriptorServiceImpl.kt
│       │   │               │   │           ├── NodeFactory.kt
│       │   │               │   │           ├── NodeSyncManager.kt
│       │   │               │   │           ├── NodeSyncManagerImpl.kt
│       │   │               │   │           ├── RootNode.kt
│       │   │               │   │           ├── RootNodeBuilder.kt
│       │   │               │   │           ├── SyncedTree.kt
│       │   │               │   │           ├── ThreadNode.kt
│       │   │               │   │           └── _fn.kt
│       │   │               │   └── fn.kt
│       │   │               ├── rework/
│       │   │               │   ├── BranchWatcher.kt
│       │   │               │   ├── ReworkEditorController.kt
│       │   │               │   ├── ReworkEditorManager.kt
│       │   │               │   ├── ReworkManager.kt
│       │   │               │   ├── ReworkWatcher.kt
│       │   │               │   └── internal/
│       │   │               │       ├── BranchWatcherImpl.kt
│       │   │               │       ├── ReworkEditorControllerImpl.kt
│       │   │               │       ├── ReworkEditorManagerImpl.kt
│       │   │               │       ├── ReworkGeneralCommentsView.kt
│       │   │               │       ├── ReworkManagerImpl.kt
│       │   │               │       └── ReworkWatcherImpl.kt
│       │   │               ├── task/
│       │   │               │   ├── FetchProjectMembersTask.kt
│       │   │               │   ├── FindApprovalTask.kt
│       │   │               │   ├── FindMergeRequestTask.kt
│       │   │               │   ├── GetAvailableUpdatesTask.kt
│       │   │               │   ├── GetCommentsTask.kt
│       │   │               │   ├── GetCommitsTask.kt
│       │   │               │   ├── GetPipelinesTask.kt
│       │   │               │   ├── RegisterProviderTask.kt
│       │   │               │   ├── RepositoryFetchAllRemotesTask.kt
│       │   │               │   └── SearchMergeRequestTask.kt
│       │   │               ├── toolWindow/
│       │   │               │   ├── CommentsToolWindowTab.kt
│       │   │               │   ├── FilesToolWindowTab.kt
│       │   │               │   ├── ReworkToolWindowTab.kt
│       │   │               │   ├── SingleMRToolWindowFactoryBase.kt
│       │   │               │   ├── SingleMRToolWindowManager.kt
│       │   │               │   └── internal/
│       │   │               │       ├── CommentsToolWindowTabImpl.kt
│       │   │               │       └── FilesToolWindowTabImpl.kt
│       │   │               ├── ui/
│       │   │               │   ├── Component.kt
│       │   │               │   ├── MainToolWindowFactoryBase.kt
│       │   │               │   ├── README.md
│       │   │               │   ├── configuration/
│       │   │               │   │   ├── AbstractConnectionsConfigurable.kt
│       │   │               │   │   ├── ConfigurationBase.kt
│       │   │               │   │   ├── ConnectionUI.kt
│       │   │               │   │   ├── GithubConnection.form
│       │   │               │   │   ├── GithubConnection.kt
│       │   │               │   │   ├── GithubConnectionsConfigurableBase.kt
│       │   │               │   │   ├── GithubProjectFinder.kt
│       │   │               │   │   ├── GitlabConnection.form
│       │   │               │   │   ├── GitlabConnection.kt
│       │   │               │   │   ├── GitlabConnectionsConfigurableBase.kt
│       │   │               │   │   ├── GitlabProjectFinder.kt
│       │   │               │   │   ├── ProjectFinderUI.kt
│       │   │               │   │   ├── SettingsConfiguration.form
│       │   │               │   │   ├── SettingsConfiguration.kt
│       │   │               │   │   └── SettingsUI.kt
│       │   │               │   ├── mergeRequest/
│       │   │               │   │   ├── AbstractMergeRequestCollection.kt
│       │   │               │   │   ├── MergeRequestCollection.kt
│       │   │               │   │   ├── MergeRequestCollectionEventListener.kt
│       │   │               │   │   ├── MergeRequestCollectionFilter.kt
│       │   │               │   │   ├── MergeRequestCollectionFilterEventListener.kt
│       │   │               │   │   ├── MergeRequestCollectionFilterUI.kt
│       │   │               │   │   ├── MergeRequestCollectionTree.kt
│       │   │               │   │   ├── MergeRequestCollectionTreeNode.kt
│       │   │               │   │   ├── MergeRequestCollectionUI.kt
│       │   │               │   │   ├── MergeRequestDetails.kt
│       │   │               │   │   ├── MergeRequestDetailsToolbar.kt
│       │   │               │   │   ├── MergeRequestDetailsToolbarUI.kt
│       │   │               │   │   ├── MergeRequestDetailsUI.kt
│       │   │               │   │   └── tab/
│       │   │               │   │       ├── MergeRequestCommitsTab.kt
│       │   │               │   │       ├── MergeRequestCommitsTabUI.kt
│       │   │               │   │       ├── MergeRequestDescriptionTab.kt
│       │   │               │   │       ├── MergeRequestDescriptionTabUI.kt
│       │   │               │   │       ├── MergeRequestInfoTab.kt
│       │   │               │   │       ├── MergeRequestInfoTabUI.kt
│       │   │               │   │       └── commit/
│       │   │               │   │           ├── CommitChanges.kt
│       │   │               │   │           ├── CommitChangesUI.kt
│       │   │               │   │           ├── CommitCollection.kt
│       │   │               │   │           ├── CommitCollectionUI.kt
│       │   │               │   │           └── CommitSelectUtil.kt
│       │   │               │   ├── panel/
│       │   │               │   │   ├── ApprovalPanel.form
│       │   │               │   │   ├── ApprovalPanel.kt
│       │   │               │   │   ├── CommitItemPanel.form
│       │   │               │   │   ├── CommitItemPanel.kt
│       │   │               │   │   ├── MergeRequestFilterPropertiesPanel.form
│       │   │               │   │   ├── MergeRequestFilterPropertiesPanel.kt
│       │   │               │   │   ├── MergeRequestInfoPanel.form
│       │   │               │   │   ├── MergeRequestInfoPanel.kt
│       │   │               │   │   ├── MergeRequestItemPanel.form
│       │   │               │   │   ├── MergeRequestItemPanel.kt
│       │   │               │   │   ├── ProjectPanel.form
│       │   │               │   │   ├── ProjectPanel.kt
│       │   │               │   │   ├── ProviderInformationPanel.form
│       │   │               │   │   ├── ProviderInformationPanel.kt
│       │   │               │   │   ├── ProviderItemPanel.form
│       │   │               │   │   ├── ProviderItemPanel.kt
│       │   │               │   │   ├── UserInfoItemPanel.form
│       │   │               │   │   └── UserInfoItemPanel.kt
│       │   │               │   ├── provider/
│       │   │               │   │   ├── ProviderCollection.kt
│       │   │               │   │   ├── ProviderCollectionList.kt
│       │   │               │   │   ├── ProviderCollectionListEventListener.kt
│       │   │               │   │   ├── ProviderCollectionListUI.kt
│       │   │               │   │   ├── ProviderCollectionToolbar.kt
│       │   │               │   │   ├── ProviderCollectionToolbarEventListener.kt
│       │   │               │   │   ├── ProviderCollectionToolbarUI.kt
│       │   │               │   │   ├── ProviderDetails.kt
│       │   │               │   │   ├── ProviderDetailsMRList.kt
│       │   │               │   │   └── ProviderDetailsUI.kt
│       │   │               │   ├── service/
│       │   │               │   │   ├── CheckoutService.kt
│       │   │               │   │   ├── CodeReviewService.kt
│       │   │               │   │   ├── DisplayChangesService.kt
│       │   │               │   │   ├── EditorStateService.kt
│       │   │               │   │   └── FetchService.kt
│       │   │               │   ├── toolWindowTab/
│       │   │               │   │   ├── HomeToolWindowTab.kt
│       │   │               │   │   ├── MergeRequestToolWindowTab.kt
│       │   │               │   │   └── UpdateInfoTab.kt
│       │   │               │   └── util/
│       │   │               │       ├── CustomSimpleToolWindowPanel.kt
│       │   │               │       ├── ImageUtil.kt
│       │   │               │       ├── Tabs.kt
│       │   │               │       ├── TabsUI.kt
│       │   │               │       ├── ToolbarUtil.kt
│       │   │               │       └── fn.kt
│       │   │               ├── util/
│       │   │               │   ├── CommentUtil.kt
│       │   │               │   ├── FileTypeUtil.kt
│       │   │               │   ├── HtmlHelper.kt
│       │   │               │   ├── RepositoryUtil.kt
│       │   │               │   └── TextChoiceUtil.kt
│       │   │               └── watcher/
│       │   │                   ├── Watcher.kt
│       │   │                   ├── WatcherManager.kt
│       │   │                   └── WatcherManagerImpl.kt
│       │   └── resources/
│       │       └── templates/
│       │           ├── mr.comment.html
│       │           ├── mr.description.html
│       │           └── update.html
│       └── test/
│           └── kotlin/
│               └── net/
│                   └── ntworld/
│                       └── mergeRequestIntegrationIde/
│                           ├── configuration/
│                           │   └── vos/
│                           │       └── GitRemotePathInfoTest.kt
│                           ├── infrastructure/
│                           │   └── DummyProjectServiceProvider.kt
│                           ├── internal/
│                           │   └── CodeReviewServiceImplTest.kt
│                           ├── mergeRequest/
│                           │   └── comments/
│                           │       └── tree/
│                           │           └── node/
│                           │               └── NodeSyncManagerImplTest.kt
│                           └── watcher/
│                               └── WatcherManagerImplTest.kt
├── merge-request-integration-ee/
│   ├── LICENSE
│   ├── build.gradle.kts
│   ├── doc/
│   │   ├── description.html
│   │   ├── release-notes.2019.3.1.html
│   │   ├── release-notes.2019.3.2.html
│   │   ├── release-notes.2019.3.3.html
│   │   ├── release-notes.2019.3.4.html
│   │   ├── release-notes.2019.3.5.html
│   │   ├── release-notes.2020.1.0.html
│   │   ├── release-notes.2020.1.1.html
│   │   ├── release-notes.2020.1.2.html
│   │   ├── release-notes.2020.1.3.html
│   │   ├── release-notes.2020.1.4.html
│   │   ├── release-notes.2020.1.5.html
│   │   ├── release-notes.2020.2.0.html
│   │   └── release-notes.2020.3.0.html
│   ├── settings.gradle.kts
│   └── src/
│       └── main/
│           ├── kotlin/
│           │   └── net/
│           │       └── ntworld/
│           │           └── mergeRequestIntegrationIdeEE/
│           │               ├── CheckLicense.kt
│           │               ├── Configuration.kt
│           │               ├── DiffExtension.kt
│           │               ├── DiffViewAddCommentAction.kt
│           │               ├── DiffViewToggleCommentsAction.kt
│           │               ├── EnterpriseApplicationServiceProvider.kt
│           │               ├── EnterpriseProjectServiceProvider.kt
│           │               ├── GithubConnectionsConfigurable.kt
│           │               ├── GitlabConnectionsConfigurable.kt
│           │               ├── MainToolWindowFactory.kt
│           │               └── SingleMRToolWindowFactory.kt
│           └── resources/
│               └── META-INF/
│                   └── plugin.xml
└── settings.gradle.kts
Condensed preview — 590 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,240K chars).
[
  {
    "path": ".gitignore",
    "chars": 164,
    "preview": "# Ignore Gradle project-specific cache directory\n.gradle\n\n# Ignore Gradle build output directory\nbuild\n\nout\n\n.kotlintest"
  },
  {
    "path": "LICENSE",
    "chars": 19538,
    "preview": "Merge Request Integration Community Edition (CE) (c) 2019-present Nhat Phan <nhat.phan@ntworld.net> (https://github.com/"
  },
  {
    "path": "README.md",
    "chars": 3535,
    "preview": "## Merge Request Integration\n\nMerge Request Integration is a plugin which helps you to do Code Review right in your IDE."
  },
  {
    "path": "build.gradle.kts",
    "chars": 1215,
    "preview": "plugins {\n    // \"org.jetbrains.kotlin.jvm\"\n    kotlin(\"jvm\") version \"1.3.50\" apply false\n\n    // \"org.jetbrains.kotlin"
  },
  {
    "path": "contracts/build.gradle.kts",
    "chars": 1381,
    "preview": "val artifactGroup: String by project\nval artifactVersion: String by project\nval jvmTarget: String by project\nval foundat"
  },
  {
    "path": "contracts/settings.gradle.kts",
    "chars": 31,
    "preview": "rootProject.name = \"contracts\"\n"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/Approval.kt",
    "chars": 338,
    "preview": "package net.ntworld.mergeRequest\n\ninterface Approval {\n    val approved: Boolean\n\n    val approvalsRequired: Int\n\n    va"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/Change.kt",
    "chars": 252,
    "preview": "package net.ntworld.mergeRequest\n\ninterface Change {\n    val oldPath: String\n    val newPath: String\n    val aMode: Stri"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/Comment.kt",
    "chars": 402,
    "preview": "package net.ntworld.mergeRequest\n\ninterface Comment {\n    val id: String\n\n    val parentId: String\n\n    val replyId: Str"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/CommentPosition.kt",
    "chars": 348,
    "preview": "package net.ntworld.mergeRequest\n\ninterface CommentPosition {\n    val baseHash: String\n\n    val startHash: String\n\n    v"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/CommentPositionChangeType.kt",
    "chars": 126,
    "preview": "package net.ntworld.mergeRequest\n\nenum class CommentPositionChangeType{\n    UNKNOWN,\n    INSERTED,\n    DELETED,\n    MODI"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/CommentPositionSource.kt",
    "chars": 171,
    "preview": "package net.ntworld.mergeRequest\n\nenum class CommentPositionSource {\n    UNKNOWN,\n    SERVER,\n    SINGLE_SIDE,\n    SIDE_"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/Commit.kt",
    "chars": 204,
    "preview": "package net.ntworld.mergeRequest\n\ninterface Commit {\n    val id: String\n\n    val message: String\n\n    val authorName: St"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/DateTime.kt",
    "chars": 61,
    "preview": "package net.ntworld.mergeRequest\n\ntypealias DateTime = String"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/DateTimeSerializer.kt",
    "chars": 682,
    "preview": "@file:UseSerializers(DateTimeSerializer::class)\n\npackage net.ntworld.mergeRequest\n\nimport kotlinx.serialization.*\nimport"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/DiffReference.kt",
    "chars": 162,
    "preview": "package net.ntworld.mergeRequest\n\ninterface DiffReference {\n    val baseHash: String\n\n    val headHash: String\n\n    val "
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/MergeRequest.kt",
    "chars": 507,
    "preview": "package net.ntworld.mergeRequest\n\ninterface MergeRequest: MergeRequestInfo {\n    val assignee: UserInfo?\n\n    val author"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/MergeRequestInfo.kt",
    "chars": 323,
    "preview": "package net.ntworld.mergeRequest\n\ninterface MergeRequestInfo {\n    val id: String\n\n    val provider: String\n\n    val pro"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/MergeRequestState.kt",
    "chars": 110,
    "preview": "package net.ntworld.mergeRequest\n\nenum class MergeRequestState {\n    ALL,\n    OPENED,\n    CLOSED,\n    MERGED\n}"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/Pipeline.kt",
    "chars": 254,
    "preview": "package net.ntworld.mergeRequest\n\ninterface Pipeline {\n    val id: String\n\n    val hash: String\n\n    val ref: String\n\n  "
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/PipelineStatus.kt",
    "chars": 134,
    "preview": "package net.ntworld.mergeRequest\n\nenum class PipelineStatus {\n    FAILED,\n    RUNNING,\n    PARTIAL_FAILED,\n    SUCCESS,\n"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/Project.kt",
    "chars": 329,
    "preview": "package net.ntworld.mergeRequest\n\ninterface Project {\n    val id: String\n\n    val provider: ProviderInfo\n\n    val name: "
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/ProjectVisibility.kt",
    "chars": 90,
    "preview": "package net.ntworld.mergeRequest\n\nenum class ProjectVisibility {\n    PUBLIC,\n    PRIVATE\n}"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/ProviderData.kt",
    "chars": 524,
    "preview": "package net.ntworld.mergeRequest\n\nimport net.ntworld.mergeRequest.api.ApiCredentials\n\ninterface ProviderData {\n    val i"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/ProviderInfo.kt",
    "chars": 372,
    "preview": "package net.ntworld.mergeRequest\n\ninterface ProviderInfo {\n    val id: String\n\n    val name: String\n\n    val iconPath: S"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/ProviderStatus.kt",
    "chars": 85,
    "preview": "package net.ntworld.mergeRequest\n\nenum class ProviderStatus {\n    ACTIVE,\n    ERROR\n}"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/User.kt",
    "chars": 137,
    "preview": "package net.ntworld.mergeRequest\n\ninterface User : UserInfo {\n    val email: String\n\n    val createdAt: DateTime\n\n    co"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/UserInfo.kt",
    "chars": 222,
    "preview": "package net.ntworld.mergeRequest\n\ninterface UserInfo {\n    val id: String\n\n    val name: String\n\n    val username: Strin"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/UserStatus.kt",
    "chars": 84,
    "preview": "package net.ntworld.mergeRequest\n\nenum class UserStatus {\n    ACTIVE,\n    INACTIVE\n}"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/ApiConnection.kt",
    "chars": 176,
    "preview": "package net.ntworld.mergeRequest.api\n\ninterface ApiConnection {\n    val url: String\n\n    val ignoreSSLCertificateErrors:"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/ApiCredentials.kt",
    "chars": 156,
    "preview": "package net.ntworld.mergeRequest.api\n\ninterface ApiCredentials : ApiConnection {\n    val projectId: String\n\n    val vers"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/ApiOptions.kt",
    "chars": 99,
    "preview": "package net.ntworld.mergeRequest.api\n\ninterface ApiOptions {\n    val enableRequestCache: Boolean\n}\n"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/ApiProvider.kt",
    "chars": 505,
    "preview": "package net.ntworld.mergeRequest.api\n\nimport net.ntworld.mergeRequest.ProviderInfo\nimport net.ntworld.mergeRequest.User\n"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/Cache.kt",
    "chars": 986,
    "preview": "package net.ntworld.mergeRequest.api\n\nimport org.joda.time.DateTime\n\ninterface Cache {\n    val defaultTTL: Int\n\n    fun "
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/CacheNotFoundException.kt",
    "chars": 80,
    "preview": "package net.ntworld.mergeRequest.api\n\nclass CacheNotFoundException : Throwable()"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/CommentApi.kt",
    "chars": 1246,
    "preview": "package net.ntworld.mergeRequest.api\n\nimport net.ntworld.mergeRequest.Comment\nimport net.ntworld.mergeRequest.CommentPos"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/CommitApi.kt",
    "chars": 173,
    "preview": "package net.ntworld.mergeRequest.api\n\nimport net.ntworld.mergeRequest.Change\n\ninterface CommitApi {\n\n    fun getChanges("
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/DraftCommentStorage.kt",
    "chars": 636,
    "preview": "package net.ntworld.mergeRequest.api\n\nimport net.ntworld.mergeRequest.Comment\nimport net.ntworld.mergeRequest.CommentPos"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/MergeRequestApi.kt",
    "chars": 1141,
    "preview": "package net.ntworld.mergeRequest.api\n\nimport net.ntworld.mergeRequest.*\nimport net.ntworld.mergeRequest.query.GetMergeRe"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/MergeRequestOrdering.kt",
    "chars": 118,
    "preview": "package net.ntworld.mergeRequest.api\n\nenum class MergeRequestOrdering {\n    RECENTLY_UPDATED,\n    NEWEST,\n    OLDEST\n}"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/ProjectApi.kt",
    "chars": 461,
    "preview": "package net.ntworld.mergeRequest.api\n\nimport net.ntworld.mergeRequest.Project\nimport net.ntworld.mergeRequest.UserInfo\n\n"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/api/UserApi.kt",
    "chars": 252,
    "preview": "package net.ntworld.mergeRequest.api\n\nimport net.ntworld.mergeRequest.User\n\ninterface UserApi {\n    // @Deprecated(\"Not "
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/command/ApproveMergeRequestCommand.kt",
    "chars": 238,
    "preview": "package net.ntworld.mergeRequest.command\n\nimport net.ntworld.foundation.cqrs.Command\n\ninterface ApproveMergeRequestComma"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/command/DeleteCommentCommand.kt",
    "chars": 277,
    "preview": "package net.ntworld.mergeRequest.command\n\nimport net.ntworld.foundation.cqrs.Command\nimport net.ntworld.mergeRequest.Com"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/command/ResolveCommentCommand.kt",
    "chars": 279,
    "preview": "package net.ntworld.mergeRequest.command\n\nimport net.ntworld.foundation.cqrs.Command\nimport net.ntworld.mergeRequest.Com"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/command/UnapproveMergeRequestCommand.kt",
    "chars": 219,
    "preview": "package net.ntworld.mergeRequest.command\n\nimport net.ntworld.foundation.cqrs.Command\n\ninterface UnapproveMergeRequestCom"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/command/UnresolveCommentCommand.kt",
    "chars": 281,
    "preview": "package net.ntworld.mergeRequest.command\n\nimport net.ntworld.foundation.cqrs.Command\nimport net.ntworld.mergeRequest.Com"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/FindApprovalQuery.kt",
    "chars": 211,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.Query\n\ninterface FindApprovalQuery : QueryBas"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/FindApprovalQueryResult.kt",
    "chars": 229,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.QueryResult\nimport net.ntworld.mergeRequest.A"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/FindMergeRequestQuery.kt",
    "chars": 218,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.Query\n\ninterface FindMergeRequestQuery: Query"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/FindMergeRequestQueryResult.kt",
    "chars": 245,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.QueryResult\nimport net.ntworld.mergeRequest.M"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetCommentsQuery.kt",
    "chars": 210,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.Query\n\ninterface GetCommentsQuery : QueryBase"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetCommentsQueryResult.kt",
    "chars": 232,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.QueryResult\nimport net.ntworld.mergeRequest.C"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetCommitsQuery.kt",
    "chars": 211,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.Query\n\ninterface GetCommitsQuery : QueryBase,"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetCommitsQueryResult.kt",
    "chars": 229,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.QueryResult\nimport net.ntworld.mergeRequest.C"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetMergeRequestFilter.kt",
    "chars": 342,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.mergeRequest.MergeRequestState\n\ninterface GetMergeRequestFilt"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetMergeRequestsQuery.kt",
    "chars": 371,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.Query\nimport net.ntworld.mergeRequest.api.Mer"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetMergeRequestsQueryResult.kt",
    "chars": 336,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.QueryResult\nimport net.ntworld.mergeRequest.M"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetPipelinesQuery.kt",
    "chars": 214,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.Query\n\ninterface GetPipelinesQuery : QueryBas"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetPipelinesQueryResult.kt",
    "chars": 237,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.QueryResult\nimport net.ntworld.mergeRequest.P"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetProjectMembersQuery.kt",
    "chars": 189,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.Query\n\ninterface GetProjectMembersQuery : Que"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/GetProjectMembersQueryResult.kt",
    "chars": 239,
    "preview": "package net.ntworld.mergeRequest.query\n\nimport net.ntworld.foundation.cqrs.QueryResult\nimport net.ntworld.mergeRequest.U"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/query/QueryBase.kt",
    "chars": 90,
    "preview": "package net.ntworld.mergeRequest.query\n\ninterface QueryBase {\n    val providerId: String\n}"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/request/CreateCommentRequest.kt",
    "chars": 425,
    "preview": "package net.ntworld.mergeRequest.request\n\nimport net.ntworld.foundation.Request\nimport net.ntworld.mergeRequest.CommentP"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/request/PublishAllCommentsRequest.kt",
    "chars": 307,
    "preview": "package net.ntworld.mergeRequest.request\n\nimport net.ntworld.foundation.Request\nimport net.ntworld.mergeRequest.response"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/request/PublishCommentsRequest.kt",
    "chars": 337,
    "preview": "package net.ntworld.mergeRequest.request\n\nimport net.ntworld.foundation.Request\nimport net.ntworld.mergeRequest.response"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/request/ReplyCommentRequest.kt",
    "chars": 384,
    "preview": "package net.ntworld.mergeRequest.request\n\nimport net.ntworld.foundation.Request\nimport net.ntworld.mergeRequest.Comment\n"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/request/UpdateCommentRequest.kt",
    "chars": 380,
    "preview": "package net.ntworld.mergeRequest.request\n\nimport net.ntworld.foundation.Request\nimport net.ntworld.mergeRequest.Comment\n"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/response/CreateCommentResponse.kt",
    "chars": 185,
    "preview": "package net.ntworld.mergeRequest.response\n\nimport net.ntworld.foundation.Response\n\ninterface CreateCommentResponse : Res"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/response/PublishAllCommentsResponse.kt",
    "chars": 181,
    "preview": "package net.ntworld.mergeRequest.response\n\nimport net.ntworld.foundation.Response\n\ninterface PublishAllCommentsResponse "
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/response/PublishCommentsResponse.kt",
    "chars": 178,
    "preview": "package net.ntworld.mergeRequest.response\n\nimport net.ntworld.foundation.Response\n\ninterface PublishCommentsResponse : R"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/response/ReplyCommentResponse.kt",
    "chars": 184,
    "preview": "package net.ntworld.mergeRequest.response\n\nimport net.ntworld.foundation.Response\n\ninterface ReplyCommentResponse : Resp"
  },
  {
    "path": "contracts/src/main/kotlin/net/ntworld/mergeRequest/response/UpdateCommentResponse.kt",
    "chars": 178,
    "preview": "package net.ntworld.mergeRequest.response\n\nimport net.ntworld.foundation.Response\n\ninterface UpdateCommentResponse : Res"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 233,
    "preview": "#Thu Oct 10 17:57:14 CEST 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER"
  },
  {
    "path": "gradle.properties",
    "chars": 710,
    "preview": "artifactGroup=net.ntworld.nhat-phan.merge-request-integration\n\n# Please also change version number in\n# net.ntworld.merg"
  },
  {
    "path": "gradlew",
    "chars": 5916,
    "preview": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0"
  },
  {
    "path": "gradlew.bat",
    "chars": 2941,
    "preview": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (th"
  },
  {
    "path": "merge-request-integration/build.gradle.kts",
    "chars": 2160,
    "preview": "val artifactGroup: String by project\nval artifactVersion: String by project\nval jvmTarget: String by project\nval foundat"
  },
  {
    "path": "merge-request-integration/settings.gradle.kts",
    "chars": 47,
    "preview": "rootProject.name = \"merge-request-integration\"\n"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/DefaultProviderStorage.kt",
    "chars": 4512,
    "preview": "package net.ntworld.mergeRequestIntegration\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntworld.mergeReques"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/MergeRequestIntegrationInfrastructure.kt",
    "chars": 384,
    "preview": "package net.ntworld.mergeRequestIntegration\n\nimport net.ntworld.foundation.InfrastructureProvider\n\nclass MergeRequestInt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/ProviderStorage.kt",
    "chars": 787,
    "preview": "package net.ntworld.mergeRequestIntegration\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntworld.mergeReques"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/_const.kt",
    "chars": 96,
    "preview": "package net.ntworld.mergeRequestIntegration\n\nconst val DEFAULT_DATETIME = \"1970-01-01T00:00:00Z\""
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/commandHandler/ApproveMergeRequestCommandHandler.kt",
    "chars": 734,
    "preview": "package net.ntworld.mergeRequestIntegration.commandHandler\n\nimport net.ntworld.foundation.Handler\nimport net.ntworld.fou"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/commandHandler/DeleteCommentCommandHandler.kt",
    "chars": 625,
    "preview": "package net.ntworld.mergeRequestIntegration.commandHandler\n\nimport net.ntworld.foundation.Handler\nimport net.ntworld.fou"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/commandHandler/ResolveCommentCommandHandler.kt",
    "chars": 629,
    "preview": "package net.ntworld.mergeRequestIntegration.commandHandler\n\nimport net.ntworld.foundation.Handler\nimport net.ntworld.fou"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/commandHandler/UnapproveMergeRequestCommandHandler.kt",
    "chars": 713,
    "preview": "package net.ntworld.mergeRequestIntegration.commandHandler\n\nimport net.ntworld.foundation.Handler\nimport net.ntworld.fou"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/commandHandler/UnresolveCommentCommandHandler.kt",
    "chars": 640,
    "preview": "package net.ntworld.mergeRequestIntegration.commandHandler\n\nimport net.ntworld.foundation.Handler\nimport net.ntworld.fou"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/exception/InvalidCacheKeyException.kt",
    "chars": 150,
    "preview": "package net.ntworld.mergeRequestIntegration.exception\n\nimport java.lang.Exception\n\nclass InvalidCacheKeyException(messag"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/exception/InvalidTTLException.kt",
    "chars": 93,
    "preview": "package net.ntworld.mergeRequestIntegration.exception\n\nclass InvalidTTLException: Exception()"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/exception/ProviderNotFoundException.kt",
    "chars": 127,
    "preview": "package net.ntworld.mergeRequestIntegration.exception\n\nimport java.lang.Exception\n\nclass ProviderNotFoundException: Exce"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/ApiOptionsImpl.kt",
    "chars": 301,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.api.ApiOptions\n\ndata class ApiOpti"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/ApprovalImpl.kt",
    "chars": 507,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.Approval\nimport net.ntworld.mergeR"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/ChangeImpl.kt",
    "chars": 371,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.Change\n\ndata class ChangeImpl(\n   "
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/CommentImpl.kt",
    "chars": 698,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.Comment\nimport net.ntworld.mergeRe"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/CommentPositionImpl.kt",
    "chars": 608,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.CommentPosition\nimport net.ntworld"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/CommitImpl.kt",
    "chars": 300,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.Commit\n\ndata class CommitImpl(\n   "
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/DiffReferenceImpl.kt",
    "chars": 252,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.DiffReference\n\ndata class DiffRefe"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/MergeRequestImpl.kt",
    "chars": 980,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.*\n\nclass MergeRequestImpl(\n    ove"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/MergeRequestInfoImpl.kt",
    "chars": 566,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.DateTime\nimport net.ntworld.mergeR"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/MergeRequestSearchResultImpl.kt",
    "chars": 377,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.MergeRequestInfo\nimport net.ntworl"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/PipelineImpl.kt",
    "chars": 459,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.DateTime\nimport net.ntworld.mergeR"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/ProjectImpl.kt",
    "chars": 556,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.Project\nimport net.ntworld.mergeRe"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/ProviderDataImpl.kt",
    "chars": 1163,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.*\nimport net.ntworld.mergeRequest."
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/UserImpl.kt",
    "chars": 473,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.DateTime\nimport net.ntworld.mergeR"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/internal/UserInfoImpl.kt",
    "chars": 617,
    "preview": "package net.ntworld.mergeRequestIntegration.internal\n\nimport net.ntworld.mergeRequest.UserInfo\nimport net.ntworld.mergeR"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/DraftCommentApi.kt",
    "chars": 3272,
    "preview": "package net.ntworld.mergeRequestIntegration.provider\n\nimport net.ntworld.mergeRequest.Comment\nimport net.ntworld.mergeRe"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/FuelClient.kt",
    "chars": 2635,
    "preview": "package net.ntworld.mergeRequestIntegration.provider\n\nimport com.github.kittinunf.fuel.core.*\nimport com.github.kittinun"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/MemoryCache.kt",
    "chars": 2499,
    "preview": "package net.ntworld.mergeRequestIntegration.provider\n\nimport net.ntworld.mergeRequest.api.Cache\nimport net.ntworld.merge"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/MemoryDraftCommentStorage.kt",
    "chars": 3134,
    "preview": "package net.ntworld.mergeRequestIntegration.provider\n\nimport net.ntworld.mergeRequest.Comment\nimport net.ntworld.mergeRe"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/MergeRequestApiDecorator.kt",
    "chars": 215,
    "preview": "package net.ntworld.mergeRequestIntegration.provider\n\nimport net.ntworld.mergeRequest.api.MergeRequestApi\n\nopen class Me"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/ProviderException.kt",
    "chars": 160,
    "preview": "package net.ntworld.mergeRequestIntegration.provider\n\nimport net.ntworld.foundation.Error\n\nclass ProviderException(val e"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/Transformer.kt",
    "chars": 117,
    "preview": "package net.ntworld.mergeRequestIntegration.provider\n\ninterface Transformer<T, R> {\n    fun transform(input: T): R\n}\n"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/Github.kt",
    "chars": 763,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github\n\nimport net.ntworld.mergeRequest.Comment\nimport net.ntworld."
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubApiProvider.kt",
    "chars": 1039,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubClient.kt",
    "chars": 1142,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github\n\nimport net.ntworld.foundation.Error\nimport net.ntworld.foun"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubFailedRequestError.kt",
    "chars": 319,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github\n\nimport net.ntworld.foundation.Error\n\ndata class GithubFaile"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubFuelClient.kt",
    "chars": 1444,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github\n\nimport com.github.kittinunf.fuel.core.HttpException\nimport "
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubMergeRequestApi.kt",
    "chars": 4851,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubProjectApi.kt",
    "chars": 1230,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubRequest.kt",
    "chars": 176,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github\n\nimport net.ntworld.mergeRequest.api.ApiCredentials\n\ninterfa"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubUserApi.kt",
    "chars": 842,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/GithubUtil.kt",
    "chars": 78,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github\n\nobject GithubUtil"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/_const.kt",
    "chars": 197,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github\n\nconst val PULL_REQUEST_STATE_OPEN = \"open\"\nconst val PULL_R"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/model/PullRequestSearchItem.kt",
    "chars": 531,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.model\n\nimport kotlinx.serialization.SerialName\nimport kotlin"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/model/SearchPullRequestResult.kt",
    "chars": 376,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.model\n\nimport kotlinx.serialization.SerialName\nimport kotlin"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/request/GithubFindCurrentUserRequest.kt",
    "chars": 458,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/request/GithubFindRepositoryRequest.kt",
    "chars": 501,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/request/GithubSearchPRsRequest.kt",
    "chars": 694,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/request/GithubSearchRepositoriesRequest.kt",
    "chars": 504,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/requestHandler/GithubFindCurrentUserRequestHandler.kt",
    "chars": 902,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.requestHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/requestHandler/GithubFindRepositoryRequestHandler.kt",
    "chars": 1100,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.requestHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/requestHandler/GithubSearchPRsRequestHandler.kt",
    "chars": 2419,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.requestHandler\n\nimport kotlinx.serialization.serializer\nimpo"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/requestHandler/GithubSearchRepositoriesRequestHandler.kt",
    "chars": 1159,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.requestHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/response/GithubFindRepositoryResponse.kt",
    "chars": 304,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.response\n\nimport net.ntworld.foundation.Error\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/response/GithubFindUserResponse.kt",
    "chars": 278,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.response\n\nimport net.ntworld.foundation.Error\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/response/GithubSearchPRsResponse.kt",
    "chars": 442,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.response\n\nimport net.ntworld.foundation.Error\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/response/GithubSearchRepositoriesResponse.kt",
    "chars": 314,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.response\n\nimport net.ntworld.foundation.Error\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/transformer/GithubRepositoryTransformer.kt",
    "chars": 1327,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.transformer\n\nimport net.ntworld.mergeRequest.Project\nimport "
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/transformer/GithubSearchPullRequestItemTransformer.kt",
    "chars": 1644,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.transformer\n\nimport net.ntworld.mergeRequest.MergeRequestInf"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/transformer/GithubUserTransformer.kt",
    "chars": 1348,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.transformer\n\nimport net.ntworld.mergeRequest.User\nimport net"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/vo/GithubMergeRequestId.kt",
    "chars": 1002,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.vo\n\ndata class GithubMergeRequestId(\n    val id: Long,\n    v"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/vo/GithubProjectId.kt",
    "chars": 1205,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.vo\n\ndata class GithubProjectId(\n    val id: Long,\n    val ow"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/github/vo/GithubUserId.kt",
    "chars": 724,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.github.vo\n\ndata class GithubUserId(\n    val id: Long,\n    val login"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/Gitlab.kt",
    "chars": 800,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.mergeRequest.Comment\nimport net.ntworld."
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabApiProvider.kt",
    "chars": 1488,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabClient.kt",
    "chars": 2460,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.foundation.Error\nimport net.ntworld.foun"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabCommentApi.kt",
    "chars": 8710,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabCommitApi.kt",
    "chars": 988,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabCredentials.kt",
    "chars": 528,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.mergeRequest.api.ApiCredentials\n\ndata cl"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabFailedRequestError.kt",
    "chars": 319,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.foundation.Error\n\ndata class GitlabFaile"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabFailedRequestException.kt",
    "chars": 160,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.foundation.Error\n\nclass GitlabFailedRequ"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabFuelClient.kt",
    "chars": 3573,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport com.github.kittinunf.fuel.core.*\nimport com.github.k"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabMergeRequestApi.kt",
    "chars": 6255,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabMergeRequestApiCache.kt",
    "chars": 1882,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.mergeRequest.MergeRequest\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabProjectApi.kt",
    "chars": 1468,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabRequest.kt",
    "chars": 176,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.mergeRequest.api.ApiCredentials\n\ninterfa"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabUserApi.kt",
    "chars": 886,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.foundation.Infrastructure\nimport net.ntw"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/GitlabUtil.kt",
    "chars": 651,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nimport net.ntworld.mergeRequest.UserStatus\nimport net.ntwor"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/_const.kt",
    "chars": 614,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab\n\nconst val GITLAB_HAS_MERGE_APPROVAL_FEATURE = \"mergeApprova"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabApproveMRCommand.kt",
    "chars": 305,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.command\n\nimport net.ntworld.foundation.cqrs.Command\nimport n"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabCreateDiffNoteCommand.kt",
    "chars": 467,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.command\n\nimport net.ntworld.foundation.cqrs.Command\nimport n"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabDeleteNoteCommand.kt",
    "chars": 336,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.command\n\nimport net.ntworld.foundation.cqrs.Command\nimport n"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabResolveNoteCommand.kt",
    "chars": 342,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.command\n\nimport net.ntworld.foundation.cqrs.Command\nimport n"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabUnapproveMRCommand.kt",
    "chars": 286,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.command\n\nimport net.ntworld.foundation.cqrs.Command\nimport n"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/command/GitlabUpdateDiffNoteCommand.kt",
    "chars": 355,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.command\n\nimport net.ntworld.foundation.cqrs.Command\nimport n"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabApproveMRCommandHandler.kt",
    "chars": 767,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabCreateDiffNoteCommandHandler.kt",
    "chars": 3152,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler\n\nimport kotlinx.serialization.Serializable\nim"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabDeleteNoteCommandHandler.kt",
    "chars": 776,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabResolveNoteCommandHandler.kt",
    "chars": 937,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabUnapproveMRCommandHandler.kt",
    "chars": 751,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/commandHandler/GitlabUpdateDiffNoteCommandHandler.kt",
    "chars": 964,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.commandHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/ApprovalModel.kt",
    "chars": 1039,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.model\n\nimport kotlinx.serialization.SerialName\nimport kotlin"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/ApproverModel.kt",
    "chars": 179,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.model\n\nimport kotlinx.serialization.Serializable\n\n@Serializa"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/GetCommentsPayload.kt",
    "chars": 1775,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.model\n\nimport kotlinx.serialization.Serializable\n\n@Serializa"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/GraphqlRequest.kt",
    "chars": 216,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.model\n\nimport kotlinx.serialization.Serializable\n\n@Serializa"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/PipelineModel.kt",
    "chars": 460,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.model\n\nimport kotlinx.serialization.SerialName\nimport kotlin"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/ReplyCommentPayload.kt",
    "chars": 174,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.model\n\nimport kotlinx.serialization.Serializable\n\n@Serializa"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/model/UserInfoModel.kt",
    "chars": 391,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.model\n\nimport kotlinx.serialization.SerialName\nimport kotlin"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabCreateNoteRequest.kt",
    "chars": 587,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabFindCurrentUserRequest.kt",
    "chars": 458,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabFindMRApprovalRequest.kt",
    "chars": 507,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabFindMRRequest.kt",
    "chars": 603,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabFindProjectRequest.kt",
    "chars": 520,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabFindUserRequest.kt",
    "chars": 568,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabGetCommitChangesRequest.kt",
    "chars": 502,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabGetMRChangesRequest.kt",
    "chars": 500,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabGetMRCommentsRequest.kt",
    "chars": 565,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabGetMRCommitsRequest.kt",
    "chars": 500,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabGetMRDiscussionsRequest.kt",
    "chars": 514,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabGetMRPipelinesRequest.kt",
    "chars": 506,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabGetProjectMembersRequest.kt",
    "chars": 479,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabReplyNoteRequest.kt",
    "chars": 566,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabSearchMRsRequest.kt",
    "chars": 987,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/request/GitlabSearchProjectsRequest.kt",
    "chars": 597,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.request\n\nimport net.ntworld.foundation.Request\nimport net.nt"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/requestHandler/GitlabCreateNoteRequestHandler.kt",
    "chars": 1426,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.requestHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/requestHandler/GitlabFindCurrentUserRequestHandler.kt",
    "chars": 926,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.requestHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/requestHandler/GitlabFindMRApprovalRequestHandler.kt",
    "chars": 1230,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.requestHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/requestHandler/GitlabFindMRRequestHandler.kt",
    "chars": 1021,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.requestHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  },
  {
    "path": "merge-request-integration/src/main/kotlin/net/ntworld/mergeRequestIntegration/provider/gitlab/requestHandler/GitlabFindProjectRequestHandler.kt",
    "chars": 1145,
    "preview": "package net.ntworld.mergeRequestIntegration.provider.gitlab.requestHandler\n\nimport net.ntworld.foundation.Handler\nimport"
  }
]

// ... and 390 more files (download for full content)

About this extraction

This page contains the full source code of the nhat-phan/merge-request-integration GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 590 files (1.1 MB), approximately 265.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!