├── .gitattributes ├── .gitignore ├── GitHubExplorer.sln ├── GitHubExplorer ├── Constants │ └── GitHubConstants.cs ├── GitHubExplorer.csproj ├── Models │ ├── GitHubIssue.cs │ ├── GitHubRepository.cs │ ├── GitHubRepositoryResponse.cs │ ├── GitHubUser.cs │ ├── GitHubUserResponse.cs │ ├── GraphQL Models │ │ ├── GraphQLError.cs │ │ ├── GraphQLRequest.cs │ │ ├── GraphQLResponse.cs │ │ └── PageInfo.cs │ └── IssuesConnection.cs ├── Program.cs └── Services │ ├── GitHubGraphQLService.cs │ └── IGitHubAPI.cs ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/xamarinstudio,visualstudio,visualstudiocode,xcode,android,macos,csharp,f#,fastlane,java,jetbrains,linux,monodevelop,objective-c,swift,sublimetext,unity 3 | 4 | ### fastlane ### 5 | # fastlane - A streamlined workflow tool for Cocoa deployment 6 | 7 | # fastlane specific 8 | fastlane/report.xml 9 | 10 | # deliver temporary files 11 | fastlane/Preview.html 12 | 13 | # snapshot generated screenshots 14 | fastlane/screenshots/**/*.png 15 | fastlane/screenshots/screenshots.html 16 | 17 | # scan temporary files 18 | fastlane/test_output 19 | 20 | 21 | ### XamarinStudio ### 22 | bin/ 23 | obj/ 24 | *.userprefs 25 | 26 | 27 | ### VisualStudioCode ### 28 | .vscode/* 29 | !.vscode/settings.json 30 | !.vscode/tasks.json 31 | !.vscode/launch.json 32 | !.vscode/extensions.json 33 | 34 | 35 | ### Xcode ### 36 | # Xcode 37 | # 38 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 39 | 40 | ## Build generated 41 | build/ 42 | DerivedData/ 43 | 44 | ## Various settings 45 | *.pbxuser 46 | !default.pbxuser 47 | *.mode1v3 48 | !default.mode1v3 49 | *.mode2v3 50 | !default.mode2v3 51 | *.perspectivev3 52 | !default.perspectivev3 53 | xcuserdata/ 54 | 55 | ## Other 56 | *.moved-aside 57 | *.xccheckout 58 | *.xcscmblueprint 59 | 60 | 61 | ### Android ### 62 | # Built application files 63 | *.apk 64 | *.ap_ 65 | 66 | # Files for the ART/Dalvik VM 67 | *.dex 68 | 69 | # Java class files 70 | *.class 71 | 72 | # Generated files 73 | gen/ 74 | out/ 75 | Resource.designer.cs 76 | 77 | # Gradle files 78 | .gradle/ 79 | 80 | # Local configuration file (sdk path, etc) 81 | local.properties 82 | 83 | # Proguard folder generated by Eclipse 84 | proguard/ 85 | 86 | # Log Files 87 | *.log 88 | 89 | # Android Studio Navigation editor temp files 90 | .navigation/ 91 | 92 | # Android Studio captures folder 93 | captures/ 94 | 95 | # Intellij 96 | *.iml 97 | .idea/workspace.xml 98 | .idea/tasks.xml 99 | .idea/libraries 100 | 101 | # Keystore files 102 | *.jks 103 | 104 | # External native build folder generated in Android Studio 2.2 and later 105 | .externalNativeBuild 106 | 107 | ### Android Patch ### 108 | gen-external-apklibs 109 | 110 | 111 | ### macOS ### 112 | *.DS_Store 113 | .AppleDouble 114 | .LSOverride 115 | 116 | # Icon must end with two \r 117 | Icon 118 | # Thumbnails 119 | ._* 120 | # Files that might appear in the root of a volume 121 | .DocumentRevisions-V100 122 | .fseventsd 123 | .Spotlight-V100 124 | .TemporaryItems 125 | .Trashes 126 | .VolumeIcon.icns 127 | .com.apple.timemachine.donotpresent 128 | # Directories potentially created on remote AFP share 129 | .AppleDB 130 | .AppleDesktop 131 | Network Trash Folder 132 | Temporary Items 133 | .apdisk 134 | 135 | 136 | ### Csharp ### 137 | ## Ignore Visual Studio temporary files, build results, and 138 | ## files generated by popular Visual Studio add-ons. 139 | ## 140 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 141 | 142 | # User-specific files 143 | *.suo 144 | *.user 145 | *.userosscache 146 | *.sln.docstates 147 | *.vcxproj.filters 148 | 149 | # User-specific files (MonoDevelop/Xamarin Studio) 150 | 151 | # Build results 152 | [Dd]ebug/ 153 | [Dd]ebugPublic/ 154 | [Rr]elease/ 155 | [Rr]eleases/ 156 | x64/ 157 | x86/ 158 | bld/ 159 | [Bb]in/ 160 | [Oo]bj/ 161 | [Ll]og/ 162 | 163 | # Visual Studio 2015 cache/options directory 164 | .vs/ 165 | # Uncomment if you have tasks that create the project's static files in wwwroot 166 | #wwwroot/ 167 | 168 | # MSTest test Results 169 | [Tt]est[Rr]esult*/ 170 | [Bb]uild[Ll]og.* 171 | 172 | # NUNIT 173 | *.VisualState.xml 174 | TestResult.xml 175 | 176 | # Build Results of an ATL Project 177 | [Dd]ebugPS/ 178 | [Rr]eleasePS/ 179 | dlldata.c 180 | 181 | # .NET Core 182 | project.lock.json 183 | project.fragment.lock.json 184 | artifacts/ 185 | **/Properties/launchSettings.json 186 | 187 | *_i.c 188 | *_p.c 189 | *_i.h 190 | *.ilk 191 | *.meta 192 | *.obj 193 | *.pch 194 | *.pdb 195 | *.pgc 196 | *.pgd 197 | *.rsp 198 | *.sbr 199 | *.tlb 200 | *.tli 201 | *.tlh 202 | *.tmp 203 | *.tmp_proj 204 | *.vspscc 205 | *.vssscc 206 | .builds 207 | *.pidb 208 | *.svclog 209 | *.scc 210 | 211 | # Chutzpah Test files 212 | _Chutzpah* 213 | 214 | # Visual C++ cache files 215 | ipch/ 216 | *.aps 217 | *.ncb 218 | *.opendb 219 | *.opensdf 220 | *.sdf 221 | *.cachefile 222 | *.VC.db 223 | *.VC.VC.opendb 224 | 225 | # Visual Studio profiler 226 | *.psess 227 | *.vsp 228 | *.vspx 229 | *.sap 230 | 231 | # TFS 2012 Local Workspace 232 | $tf/ 233 | 234 | # Guidance Automation Toolkit 235 | *.gpState 236 | 237 | # ReSharper is a .NET coding add-in 238 | _ReSharper*/ 239 | *.[Rr]e[Ss]harper 240 | *.DotSettings.user 241 | 242 | # JustCode is a .NET coding add-in 243 | .JustCode 244 | 245 | # TeamCity is a build add-in 246 | _TeamCity* 247 | 248 | # DotCover is a Code Coverage Tool 249 | *.dotCover 250 | 251 | # Visual Studio code coverage results 252 | *.coverage 253 | *.coveragexml 254 | 255 | # NCrunch 256 | _NCrunch_* 257 | .*crunch*.local.xml 258 | nCrunchTemp_* 259 | 260 | # MightyMoose 261 | *.mm.* 262 | AutoTest.Net/ 263 | 264 | # Web workbench (sass) 265 | .sass-cache/ 266 | 267 | # Installshield output folder 268 | [Ee]xpress/ 269 | 270 | # DocProject is a documentation generator add-in 271 | DocProject/buildhelp/ 272 | DocProject/Help/*.HxT 273 | DocProject/Help/*.HxC 274 | DocProject/Help/*.hhc 275 | DocProject/Help/*.hhk 276 | DocProject/Help/*.hhp 277 | DocProject/Help/Html2 278 | DocProject/Help/html 279 | 280 | # Click-Once directory 281 | publish/ 282 | 283 | # Publish Web Output 284 | *.[Pp]ublish.xml 285 | *.azurePubxml 286 | # TODO: Comment the next line if you want to checkin your web deploy settings 287 | # but database connection strings (with potential passwords) will be unencrypted 288 | *.pubxml 289 | *.publishproj 290 | 291 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 292 | # checkin your Azure Web App publish settings, but sensitive information contained 293 | # in these scripts will be unencrypted 294 | PublishScripts/ 295 | 296 | # NuGet Packages 297 | *.nupkg 298 | # The packages folder can be ignored because of Package Restore 299 | **/packages/* 300 | # except build/, which is used as an MSBuild target. 301 | !**/packages/build/ 302 | # Uncomment if necessary however generally it will be regenerated when needed 303 | #!**/packages/repositories.config 304 | # NuGet v3's project.json files produces more ignoreable files 305 | *.nuget.props 306 | *.nuget.targets 307 | 308 | # Microsoft Azure Build Output 309 | csx/ 310 | *.build.csdef 311 | 312 | # Microsoft Azure Emulator 313 | ecf/ 314 | rcf/ 315 | 316 | # Windows Store app package directories and files 317 | AppPackages/ 318 | BundleArtifacts/ 319 | _pkginfo.txt 320 | 321 | # Visual Studio cache files 322 | # files ending in .cache can be ignored 323 | *.[Cc]ache 324 | # but keep track of directories ending in .cache 325 | !*.[Cc]ache/ 326 | 327 | # Others 328 | ClientBin/ 329 | ~$* 330 | *~ 331 | *.dbmdl 332 | *.dbproj.schemaview 333 | *.jfm 334 | *.pfx 335 | *.publishsettings 336 | node_modules/ 337 | orleans.codegen.cs 338 | 339 | # Since there are multiple workflows, uncomment next line to ignore bower_components 340 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 341 | #bower_components/ 342 | 343 | # RIA/Silverlight projects 344 | Generated_Code/ 345 | 346 | # Backup & report files from converting an old project file 347 | # to a newer Visual Studio version. Backup files are not needed, 348 | # because we have git ;-) 349 | _UpgradeReport_Files/ 350 | Backup*/ 351 | UpgradeLog*.XML 352 | UpgradeLog*.htm 353 | 354 | # SQL Server files 355 | *.mdf 356 | *.ldf 357 | 358 | # Business Intelligence projects 359 | *.rdl.data 360 | *.bim.layout 361 | *.bim_*.settings 362 | 363 | # Microsoft Fakes 364 | FakesAssemblies/ 365 | 366 | # GhostDoc plugin setting file 367 | *.GhostDoc.xml 368 | 369 | # Node.js Tools for Visual Studio 370 | .ntvs_analysis.dat 371 | 372 | # Visual Studio 6 build log 373 | *.plg 374 | 375 | # Visual Studio 6 workspace options file 376 | *.opt 377 | 378 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 379 | *.vbw 380 | 381 | # Visual Studio LightSwitch build output 382 | **/*.HTMLClient/GeneratedArtifacts 383 | **/*.DesktopClient/GeneratedArtifacts 384 | **/*.DesktopClient/ModelManifest.xml 385 | **/*.Server/GeneratedArtifacts 386 | **/*.Server/ModelManifest.xml 387 | _Pvt_Extensions 388 | 389 | # Paket dependency manager 390 | .paket/paket.exe 391 | paket-files/ 392 | 393 | # FAKE - F# Make 394 | .fake/ 395 | 396 | # JetBrains Rider 397 | .idea/ 398 | *.sln.iml 399 | 400 | # CodeRush 401 | .cr/ 402 | 403 | # Python Tools for Visual Studio (PTVS) 404 | __pycache__/ 405 | *.pyc 406 | 407 | # Cake - Uncomment if you are using it 408 | # tools/ 409 | 410 | 411 | ### F# ### 412 | lib/debug 413 | lib/release 414 | Debug 415 | obj 416 | bin 417 | *.exe 418 | !.paket/paket.bootstrapper.exe 419 | 420 | 421 | ### SublimeText ### 422 | # cache files for sublime text 423 | *.tmlanguage.cache 424 | *.tmPreferences.cache 425 | *.stTheme.cache 426 | 427 | # workspace files are user-specific 428 | *.sublime-workspace 429 | 430 | # project files should be checked into the repository, unless a significant 431 | # proportion of contributors will probably not be using SublimeText 432 | # *.sublime-project 433 | 434 | # sftp configuration file 435 | sftp-config.json 436 | 437 | # Package control specific files 438 | Package Control.last-run 439 | Package Control.ca-list 440 | Package Control.ca-bundle 441 | Package Control.system-ca-bundle 442 | Package Control.cache/ 443 | Package Control.ca-certs/ 444 | bh_unicode_properties.cache 445 | 446 | # Sublime-github package stores a github token in this file 447 | # https://packagecontrol.io/packages/sublime-github 448 | GitHub.sublime-settings 449 | 450 | 451 | ### Swift ### 452 | # Xcode 453 | # 454 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 455 | 456 | ## Build generated 457 | 458 | ## Various settings 459 | 460 | ## Other 461 | *.xcuserstate 462 | 463 | ## Obj-C/Swift specific 464 | *.hmap 465 | *.ipa 466 | *.dSYM.zip 467 | *.dSYM 468 | 469 | ## Playgrounds 470 | timeline.xctimeline 471 | playground.xcworkspace 472 | 473 | # Swift Package Manager 474 | # 475 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 476 | # Packages/ 477 | .build/ 478 | 479 | # CocoaPods 480 | # 481 | # We recommend against adding the Pods directory to your .gitignore. However 482 | # you should judge for yourself, the pros and cons are mentioned at: 483 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 484 | # 485 | # Pods/ 486 | 487 | # Carthage 488 | # 489 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 490 | # Carthage/Checkouts 491 | 492 | Carthage/Build 493 | 494 | # fastlane 495 | # 496 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 497 | # screenshots whenever they are needed. 498 | # For more information about the recommended setup visit: 499 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 500 | 501 | fastlane/screenshots 502 | 503 | 504 | ### JetBrains ### 505 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 506 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 507 | 508 | # User-specific stuff: 509 | 510 | # Sensitive or high-churn files: 511 | .idea/dataSources/ 512 | .idea/dataSources.ids 513 | .idea/dataSources.xml 514 | .idea/dataSources.local.xml 515 | .idea/sqlDataSources.xml 516 | .idea/dynamic.xml 517 | .idea/uiDesigner.xml 518 | 519 | # Gradle: 520 | .idea/gradle.xml 521 | 522 | # Mongo Explorer plugin: 523 | .idea/mongoSettings.xml 524 | 525 | ## File-based project format: 526 | *.iws 527 | 528 | ## Plugin-specific files: 529 | 530 | # IntelliJ 531 | /out/ 532 | 533 | # mpeltonen/sbt-idea plugin 534 | .idea_modules/ 535 | 536 | # JIRA plugin 537 | atlassian-ide-plugin.xml 538 | 539 | # Crashlytics plugin (for Android Studio and IntelliJ) 540 | com_crashlytics_export_strings.xml 541 | crashlytics.properties 542 | crashlytics-build.properties 543 | fabric.properties 544 | 545 | ### JetBrains Patch ### 546 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 547 | 548 | # *.iml 549 | # modules.xml 550 | # .idea/misc.xml 551 | # *.ipr 552 | 553 | 554 | ### Linux ### 555 | 556 | # temporary files which can be created if a process still has a handle open of a deleted file 557 | .fuse_hidden* 558 | 559 | # KDE directory preferences 560 | .directory 561 | 562 | # Linux trash folder which might appear on any partition or disk 563 | .Trash-* 564 | 565 | # .nfs files are created when an open file is removed but is still being accessed 566 | .nfs* 567 | 568 | 569 | ### MonoDevelop ### 570 | #User Specific 571 | *.usertasks 572 | 573 | #Mono Project Files 574 | *.resources 575 | test-results/ 576 | 577 | 578 | ### Objective-C ### 579 | # Xcode 580 | # 581 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 582 | 583 | ## Build generated 584 | 585 | ## Various settings 586 | 587 | ## Other 588 | 589 | ## Obj-C/Swift specific 590 | 591 | # CocoaPods 592 | # 593 | # We recommend against adding the Pods directory to your .gitignore. However 594 | # you should judge for yourself, the pros and cons are mentioned at: 595 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 596 | # 597 | # Pods/ 598 | 599 | # Carthage 600 | # 601 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 602 | # Carthage/Checkouts 603 | 604 | 605 | # fastlane 606 | # 607 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 608 | # screenshots whenever they are needed. 609 | # For more information about the recommended setup visit: 610 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 611 | 612 | 613 | # Code Injection 614 | # 615 | # After new code Injection tools there's a generated folder /iOSInjectionProject 616 | # https://github.com/johnno1962/injectionforxcode 617 | 618 | iOSInjectionProject/ 619 | 620 | ### Objective-C Patch ### 621 | 622 | 623 | ### Unity ### 624 | /[Ll]ibrary/ 625 | /[Tt]emp/ 626 | /[Oo]bj/ 627 | /[Bb]uild/ 628 | /[Bb]uilds/ 629 | /Assets/AssetStoreTools* 630 | 631 | # Autogenerated VS/MD/Consulo solution and project files 632 | ExportedObj/ 633 | .consulo/*.csproj 634 | .consulo/*.unityproj 635 | .consulo/*.sln 636 | .consulo/*.booproj 637 | .consulo/*.svd 638 | 639 | 640 | # Unity3D generated meta files 641 | *.pidb.meta 642 | 643 | # Unity3D Generated File On Crash Reports 644 | sysinfo.txt 645 | 646 | # Builds 647 | *.unitypackage 648 | 649 | 650 | ### Java ### 651 | 652 | # BlueJ files 653 | *.ctxt 654 | 655 | # Mobile Tools for Java (J2ME) 656 | .mtj.tmp/ 657 | 658 | # Package Files # 659 | *.jar 660 | *.war 661 | *.ear 662 | 663 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 664 | hs_err_pid* 665 | 666 | 667 | ### VisualStudio ### 668 | ## Ignore Visual Studio temporary files, build results, and 669 | ## files generated by popular Visual Studio add-ons. 670 | ## 671 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 672 | 673 | # User-specific files 674 | 675 | # User-specific files (MonoDevelop/Xamarin Studio) 676 | 677 | # Build results 678 | 679 | # Visual Studio 2015 cache/options directory 680 | # Uncomment if you have tasks that create the project's static files in wwwroot 681 | #wwwroot/ 682 | 683 | # MSTest test Results 684 | 685 | # NUNIT 686 | 687 | # Build Results of an ATL Project 688 | 689 | # .NET Core 690 | 691 | 692 | # Chutzpah Test files 693 | 694 | # Visual C++ cache files 695 | 696 | # Visual Studio profiler 697 | 698 | # TFS 2012 Local Workspace 699 | 700 | # Guidance Automation Toolkit 701 | 702 | # ReSharper is a .NET coding add-in 703 | 704 | # JustCode is a .NET coding add-in 705 | 706 | # TeamCity is a build add-in 707 | 708 | # DotCover is a Code Coverage Tool 709 | 710 | # Visual Studio code coverage results 711 | 712 | # NCrunch 713 | 714 | # MightyMoose 715 | 716 | # Web workbench (sass) 717 | 718 | # Installshield output folder 719 | 720 | # DocProject is a documentation generator add-in 721 | 722 | # Click-Once directory 723 | 724 | # Publish Web Output 725 | # TODO: Comment the next line if you want to checkin your web deploy settings 726 | # but database connection strings (with potential passwords) will be unencrypted 727 | 728 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 729 | # checkin your Azure Web App publish settings, but sensitive information contained 730 | # in these scripts will be unencrypted 731 | 732 | # NuGet Packages 733 | # The packages folder can be ignored because of Package Restore 734 | # except build/, which is used as an MSBuild target. 735 | # Uncomment if necessary however generally it will be regenerated when needed 736 | #!**/packages/repositories.config 737 | # NuGet v3's project.json files produces more ignoreable files 738 | 739 | # Microsoft Azure Build Output 740 | 741 | # Microsoft Azure Emulator 742 | 743 | # Windows Store app package directories and files 744 | 745 | # Visual Studio cache files 746 | # files ending in .cache can be ignored 747 | # but keep track of directories ending in .cache 748 | 749 | # Others 750 | 751 | # Since there are multiple workflows, uncomment next line to ignore bower_components 752 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 753 | #bower_components/ 754 | 755 | # RIA/Silverlight projects 756 | 757 | # Backup & report files from converting an old project file 758 | # to a newer Visual Studio version. Backup files are not needed, 759 | # because we have git ;-) 760 | 761 | # SQL Server files 762 | 763 | # Business Intelligence projects 764 | 765 | # Microsoft Fakes 766 | 767 | # GhostDoc plugin setting file 768 | 769 | # Node.js Tools for Visual Studio 770 | 771 | # Visual Studio 6 build log 772 | 773 | # Visual Studio 6 workspace options file 774 | 775 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 776 | 777 | # Visual Studio LightSwitch build output 778 | 779 | # Paket dependency manager 780 | 781 | # FAKE - F# Make 782 | 783 | # JetBrains Rider 784 | 785 | # CodeRush 786 | 787 | # Python Tools for Visual Studio (PTVS) 788 | 789 | # Cake - Uncomment if you are using it 790 | # tools/ 791 | 792 | ### VisualStudio Patch ### 793 | 794 | # End of https://www.gitignore.io/api/xamarinstudio,visualstudio,visualstudiocode,xcode,android,macos,csharp,f#,fastlane,java,jetbrains,linux,monodevelop,objective-c,swift,sublimetext,unity 795 | -------------------------------------------------------------------------------- /GitHubExplorer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHubExplorer", "GitHubExplorer\GitHubExplorer.csproj", "{46A13852-CF96-4C60-8D53-2FF3648E85CE}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|x86 = Debug|x86 9 | Release|x86 = Release|x86 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {46A13852-CF96-4C60-8D53-2FF3648E85CE}.Debug|x86.ActiveCfg = Debug|Any CPU 13 | {46A13852-CF96-4C60-8D53-2FF3648E85CE}.Debug|x86.Build.0 = Debug|Any CPU 14 | {46A13852-CF96-4C60-8D53-2FF3648E85CE}.Release|x86.ActiveCfg = Release|Any CPU 15 | {46A13852-CF96-4C60-8D53-2FF3648E85CE}.Release|x86.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | EndGlobal 18 | -------------------------------------------------------------------------------- /GitHubExplorer/Constants/GitHubConstants.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace GitHubExplorer 5 | { 6 | public static class GitHubConstants 7 | { 8 | #error Missing Token, Follow these steps to create your Personal Access Token: https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/#creating-a-token 9 | public const string PersonalAccessToken = "Enter Token Here"; 10 | public const string APIUrl = "https://api.github.com/graphql"; 11 | 12 | public static ReadOnlyDictionary GitHubRepoDictionary { get; } = new(new Dictionary 13 | { 14 | {"Newtonsoft.Json","jamesnk"}, 15 | {"Newtonsoft.Json.Schema","jamesnk"}, 16 | 17 | {"AsyncAwaitBestPractices","brminnick"}, 18 | {"ImproveXamarinBuildTimes","brminnick"}, 19 | {"FaceOff","brminnick"}, 20 | {"GitTrends","brminnick"}, 21 | {"XamList","brminnick"}, 22 | {"TextMood","brminnick"}, 23 | 24 | {"allReady","BillWagner"}, 25 | {"crisischeckin","BillWagner"}, 26 | 27 | {"TensorFlowSharp","migueldeicaza"}, 28 | {"gui.cs","migueldeicaza"}, 29 | {"MonoTouch.Dialog","migueldeicaza"}, 30 | {"mono-wasm","migueldeicaza"}, 31 | {"TweetStation","migueldeicaza"}, 32 | {"redis-sharp","migueldeicaza"}, 33 | 34 | {"ModernHttpClient","paulcbetts"}, 35 | {"SassAndCoffee","paulcbetts"}, 36 | {"starter-mobile","paulcbetts"}, 37 | {"grunt-build-atom-shell","paulcbetts"}, 38 | {"spawn-rx","paulcbetts"}, 39 | {"LinqToAwait","paulcbetts"}, 40 | 41 | {"CodeBucket","thedillonb"}, 42 | {"RepoStumble","thedillonb"}, 43 | {"http-shutdown","thedillonb"}, 44 | {"rails-angularjs-simple-forum","thedillonb"}, 45 | {"twitter-cashtag-heatmap","thedillonb"}, 46 | {"MonoTouch.SlideoutNavigation","thedillonb"}, 47 | 48 | {"WebEssentials2013","madskristensen"}, 49 | {"MiniBlog","madskristensen"}, 50 | {"WebEssentials2015","madskristensen"}, 51 | {"WebCompiler","madskristensen"}, 52 | {"Miniblog.Core","madskristensen"}, 53 | {"ShortcutExporter","madskristensen"}, 54 | 55 | {"Xamarin.Plugins","jamesmontemagno"}, 56 | {"Hanselman.Forms","jamesmontemagno"}, 57 | {"MeetupManager","jamesmontemagno"}, 58 | {"Xam.NavDrawer","jamesmontemagno"}, 59 | 60 | {"PushSharp","Redth"}, 61 | {"ZXing.Net.Mobile","Redth"}, 62 | {"APNS-Sharp","Redth"}, 63 | {"FlamedTVLauncher","Redth"}, 64 | {"AndHUD","Redth"}, 65 | {"HttpTwo","Redth"}, 66 | }); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /GitHubExplorer/GitHubExplorer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | latest 7 | enable 8 | 9 | 10 | 0 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /GitHubExplorer/Models/GitHubIssue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace GitHubExplorer 5 | { 6 | public class GitHubIssue 7 | { 8 | public GitHubIssue(string title, string body, DateTimeOffset createdAt, DateTimeOffset? closedAt, string state) 9 | { 10 | Title = title; 11 | Body = body; 12 | CreatedAt = createdAt; 13 | ClosedAt = closedAt; 14 | State = state; 15 | } 16 | 17 | public string Title { get; } 18 | 19 | public string Body { get; } 20 | 21 | public DateTimeOffset CreatedAt { get; } 22 | 23 | public DateTimeOffset? ClosedAt { get; } 24 | 25 | public string State { get; } 26 | 27 | public override string ToString() 28 | { 29 | var stringBuilder = new StringBuilder(); 30 | stringBuilder.AppendLine($"{nameof(Title)}: {Title}"); 31 | stringBuilder.AppendLine($"{nameof(CreatedAt)}: {CreatedAt}"); 32 | stringBuilder.AppendLine($"{nameof(ClosedAt)}: {ClosedAt}"); 33 | stringBuilder.AppendLine($"{nameof(State)}: {State}"); 34 | 35 | return stringBuilder.ToString(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /GitHubExplorer/Models/GitHubRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace GitHubExplorer 4 | { 5 | public class GitHubRepository 6 | { 7 | public GitHubRepository(string name, string description, long forkCount, IssuesConnection? issues, Owner owner, Stargazers stargazers) 8 | { 9 | Name = name; 10 | Description = description; 11 | ForkCount = forkCount; 12 | Owner = owner.Login; 13 | Issues = issues; 14 | Stargazers = stargazers.TotalCount; 15 | } 16 | 17 | public string Owner { get; } 18 | 19 | public int? Stargazers { get; } 20 | 21 | public string Name { get; } 22 | 23 | public string Description { get; } 24 | 25 | public long ForkCount { get; } 26 | 27 | public IssuesConnection? Issues { get; } 28 | 29 | public override string ToString() 30 | { 31 | var stringBuilder = new StringBuilder(); 32 | stringBuilder.AppendLine($"{nameof(Name)}: {Name}"); 33 | stringBuilder.AppendLine($"{nameof(Owner)}: {Owner}"); 34 | stringBuilder.AppendLine($"{nameof(Description)}: {Description}"); 35 | stringBuilder.AppendLine($"{nameof(ForkCount)}: {ForkCount}"); 36 | stringBuilder.AppendLine($"{nameof(Stargazers)}: {Stargazers}"); 37 | 38 | return stringBuilder.ToString(); 39 | } 40 | 41 | } 42 | 43 | public class Owner 44 | { 45 | public Owner(string login) => Login = login; 46 | 47 | public string Login { get; } 48 | } 49 | 50 | public class Stargazers 51 | { 52 | public Stargazers(int totalCount) => TotalCount = totalCount; 53 | 54 | public int TotalCount { get; } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /GitHubExplorer/Models/GitHubRepositoryResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace GitHubExplorer 4 | { 5 | public class GitHubRepositoryResponse 6 | { 7 | public GitHubRepositoryResponse(GitHubRepository repository) => Repository = repository; 8 | 9 | [JsonProperty("repository")] 10 | public GitHubRepository Repository { get; } 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /GitHubExplorer/Models/GitHubUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace GitHubExplorer 5 | { 6 | public class GitHubUser 7 | { 8 | public GitHubUser(string name, string company, DateTimeOffset createdAt, GitHubFollowers followers) 9 | { 10 | Name = name; 11 | Company = company; 12 | AccountCreationDate = createdAt; 13 | FollowerCount = followers.Count; 14 | } 15 | 16 | public string Name { get; } 17 | 18 | public string Company { get; } 19 | 20 | public DateTimeOffset AccountCreationDate { get; } 21 | 22 | public int FollowerCount { get; } 23 | 24 | public override string ToString() 25 | { 26 | var stringBuilder = new StringBuilder(); 27 | stringBuilder.AppendLine($"{nameof(Name)}: {Name}"); 28 | stringBuilder.AppendLine($"{nameof(Company)}: {Company}"); 29 | stringBuilder.AppendLine($"{nameof(FollowerCount)}: {FollowerCount}"); 30 | stringBuilder.AppendLine($"{nameof(AccountCreationDate)}: {AccountCreationDate}"); 31 | 32 | return stringBuilder.ToString(); 33 | } 34 | } 35 | 36 | public class GitHubFollowers 37 | { 38 | public GitHubFollowers(int count) => Count = count; 39 | 40 | public int Count { get; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /GitHubExplorer/Models/GitHubUserResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace GitHubExplorer 4 | { 5 | public class GitHubUserResponse 6 | { 7 | public GitHubUserResponse(GitHubUser user) => User = user; 8 | 9 | [JsonProperty("user")] 10 | public GitHubUser User { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /GitHubExplorer/Models/GraphQL Models/GraphQLError.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace GitHubExplorer 6 | { 7 | class GraphQLError 8 | { 9 | public GraphQLError(string message, GraphQLLocation[] locations) => (Message, Locations) = (message, locations); 10 | 11 | public string Message { get; } 12 | 13 | public GraphQLLocation[] Locations { get; } 14 | 15 | [JsonExtensionData] 16 | public Dictionary AdditonalEntries { get; set; } = new(); 17 | } 18 | 19 | class GraphQLLocation 20 | { 21 | public GraphQLLocation(long line, long column) => (Line, Column) = (line, column); 22 | 23 | public long Line { get; } 24 | public long Column { get; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /GitHubExplorer/Models/GraphQL Models/GraphQLRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace GitHubExplorer 4 | { 5 | class GraphQLRequest 6 | { 7 | public GraphQLRequest(string query, string? variables = null) => 8 | (Query, Variables) = (query, variables ?? string.Empty); 9 | 10 | [JsonProperty("query")] 11 | public string Query { get; } 12 | 13 | [JsonProperty("variables")] 14 | public string Variables { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /GitHubExplorer/Models/GraphQL Models/GraphQLResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace GitHubExplorer 4 | { 5 | class GraphQLResponse 6 | { 7 | public GraphQLResponse(T data, GraphQLError[] errors) => (Data, Errors) = (data, errors); 8 | 9 | [JsonProperty("data")] 10 | public T Data { get; } 11 | 12 | [JsonProperty("errors")] 13 | public GraphQLError[] Errors { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /GitHubExplorer/Models/GraphQL Models/PageInfo.cs: -------------------------------------------------------------------------------- 1 | namespace GitHubExplorer 2 | { 3 | public class PageInfo 4 | { 5 | public PageInfo(string endCursor, bool hasNextPage, bool hasPreviousPage, string startCursor) 6 | { 7 | EndCursor = endCursor; 8 | HasNextPage = hasNextPage; 9 | HasPreviousPage = hasPreviousPage; 10 | StartCursor = startCursor; 11 | } 12 | 13 | public string EndCursor { get; } 14 | 15 | public bool HasNextPage { get; } 16 | 17 | public bool HasPreviousPage { get; } 18 | 19 | public string StartCursor { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GitHubExplorer/Models/IssuesConnection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace GitHubExplorer 4 | { 5 | public class IssuesConnection 6 | { 7 | public IssuesConnection(GitHubIssue[] nodes, PageInfo pageInfo) => 8 | (IssueList, PageInfo) = (nodes, pageInfo); 9 | 10 | public IReadOnlyList IssueList { get; } 11 | 12 | public PageInfo PageInfo { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /GitHubExplorer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace GitHubExplorer 7 | { 8 | class MainClass 9 | { 10 | public static async Task Main() 11 | { 12 | var username = GitHubConstants.GitHubRepoDictionary.Values.First(); 13 | var repositoryName = GitHubConstants.GitHubRepoDictionary.Keys.First(); 14 | 15 | var gitHubUser = await GitHubGraphQLService.GetUser(username).ConfigureAwait(false); 16 | Console.WriteLine(gitHubUser); 17 | 18 | var gitHubRepository = await GitHubGraphQLService.GetRepository(username, repositoryName).ConfigureAwait(false); 19 | Console.WriteLine(gitHubRepository); 20 | 21 | try 22 | { 23 | var cancellationTokenSournce = new CancellationTokenSource(TimeSpan.FromSeconds(3)); 24 | 25 | await foreach (var issueList in GitHubGraphQLService.GetRepositoryIssues(username, repositoryName, cancellationTokenSournce.Token)) 26 | { 27 | foreach (var issue in issueList) 28 | Console.WriteLine(issue); 29 | } 30 | } 31 | catch(OperationCanceledException) 32 | { 33 | Console.WriteLine("GetRepositories Cancelled"); 34 | } 35 | 36 | Console.WriteLine("Completed. Press Any Key."); 37 | Console.ReadLine(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /GitHubExplorer/Services/GitHubGraphQLService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Runtime.CompilerServices; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Polly; 9 | using Refit; 10 | 11 | namespace GitHubExplorer 12 | { 13 | static class GitHubGraphQLService 14 | { 15 | readonly static Lazy _githubApiClientHolder = new Lazy(() => RestService.For(CreateHttpClient())); 16 | 17 | static IGitHubAPI GitHubApiClient => _githubApiClientHolder.Value; 18 | 19 | public static async IAsyncEnumerable GetUsers(IEnumerable userList) 20 | { 21 | var getUserTaskList = userList.Select(GetUser).ToList(); 22 | 23 | while (getUserTaskList.Any()) 24 | { 25 | var finishedGetUserTask = await Task.WhenAny(getUserTaskList).ConfigureAwait(false); 26 | getUserTaskList.Remove(finishedGetUserTask); 27 | 28 | var user = await finishedGetUserTask.ConfigureAwait(false); 29 | 30 | yield return user; 31 | } 32 | } 33 | 34 | public static async IAsyncEnumerable GetRepositories(Dictionary repositoryDictionary) 35 | { 36 | var getRepositoryTaskList = repositoryDictionary.Select(x => GetRepository(x.Value, x.Key)).ToList(); 37 | 38 | while (getRepositoryTaskList.Any()) 39 | { 40 | var finishedGetRepositoryTask = await Task.WhenAny(getRepositoryTaskList).ConfigureAwait(false); 41 | getRepositoryTaskList.Remove(finishedGetRepositoryTask); 42 | 43 | var repository = await finishedGetRepositoryTask.ConfigureAwait(false); 44 | 45 | yield return repository; 46 | } 47 | } 48 | 49 | public static async Task GetUser(string username) 50 | { 51 | var requestString = "query { user(login: \"" + username + "\"){ name,company,createdAt, followers{ totalCount }}}"; 52 | 53 | var data = await ExecuteGraphQLRequest(() => GitHubApiClient.UserQuery(new GraphQLRequest(requestString))).ConfigureAwait(false); 54 | 55 | return data.User; 56 | } 57 | 58 | public static async Task GetRepository(string repositoryOwner, string repositoryName) 59 | { 60 | var requestString = "query { repository(owner:\"" + repositoryOwner + "\" name:\"" + repositoryName + "\"){ name, description, forkCount, owner { login }, stargazers { totalCount }}}"; 61 | 62 | var data = await ExecuteGraphQLRequest(() => GitHubApiClient.RepositoryQuery(new GraphQLRequest(requestString))).ConfigureAwait(false); 63 | 64 | return data.Repository; 65 | } 66 | 67 | public static async IAsyncEnumerable> GetRepositoryIssues(string repositoryOwner, string repositoryName, [EnumeratorCancellation] CancellationToken cancellationToken, int numberOfIssuesPerRequest = 100) 68 | { 69 | IssuesConnection? issuesConnection = null; 70 | 71 | do 72 | { 73 | issuesConnection = await GetIssueConnection(repositoryOwner, repositoryName, numberOfIssuesPerRequest, issuesConnection?.PageInfo?.EndCursor).ConfigureAwait(false); 74 | 75 | if (issuesConnection?.IssueList is not null) 76 | yield return issuesConnection.IssueList; 77 | 78 | cancellationToken.ThrowIfCancellationRequested(); 79 | } 80 | while (issuesConnection?.PageInfo?.HasNextPage is true); 81 | } 82 | 83 | static async Task GetIssueConnection(string repositoryOwner, string repositoryName, int numberOfIssuesPerRequest, string? endCursor) 84 | { 85 | var endCursorString = string.IsNullOrWhiteSpace(endCursor) ? string.Empty : "after: \"" + endCursor + "\""; 86 | 87 | var requestString = "query { repository(owner:\"" + repositoryOwner + "\" name:\"" + repositoryName + "\"){ name, description, forkCount, owner { login }, stargazers { totalCount } issues(first:" + numberOfIssuesPerRequest + endCursorString + "){ nodes { title, body, createdAt, closedAt, state }, pageInfo { endCursor, hasNextPage, hasPreviousPage, startCursor }}}}"; 88 | 89 | var data = await ExecuteGraphQLRequest(() => GitHubApiClient.RepositoryIssuesQuery(new GraphQLRequest(requestString))).ConfigureAwait(false); 90 | 91 | return data.Repository.Issues; 92 | } 93 | 94 | static async Task ExecuteGraphQLRequest(Func>> action, int numRetries = 3) 95 | { 96 | var response = await Policy.Handle().WaitAndRetryAsync(numRetries, pollyRetryAttempt).ExecuteAsync(action).ConfigureAwait(false); 97 | 98 | if (response.Errors != null) 99 | throw new AggregateException(response.Errors.Select(x => new Exception(x.Message))); 100 | 101 | return response.Data; 102 | 103 | static TimeSpan pollyRetryAttempt(int attemptNumber) => TimeSpan.FromSeconds(Math.Pow(2, attemptNumber)); 104 | } 105 | 106 | static HttpClient CreateHttpClient() => new(new HttpClientHandler { AutomaticDecompression = System.Net.DecompressionMethods.GZip }) 107 | { 108 | BaseAddress = new Uri(GitHubConstants.APIUrl) 109 | }; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /GitHubExplorer/Services/IGitHubAPI.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Refit; 3 | 4 | namespace GitHubExplorer 5 | { 6 | [Headers("Accept-Encoding: gzip", 7 | "Content-Type: application/json", 8 | "Authorization: bearer " + GitHubConstants.PersonalAccessToken, 9 | "User-Agent: GitHubExplorer")] 10 | interface IGitHubAPI 11 | { 12 | [Post("")] 13 | Task> UserQuery([Body] GraphQLRequest request); 14 | 15 | [Post("")] 16 | Task> RepositoryQuery([Body] GraphQLRequest request); 17 | 18 | [Post("")] 19 | Task> RepositoryIssuesQuery([Body] GraphQLRequest request); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Brandon Minnick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Explorer 2 | Console App to explore the GitHub GraphQL API 3 | 4 | ![Console Screenshot](https://user-images.githubusercontent.com/13558917/50998962-e8f1cf00-14dd-11e9-81e9-283cd1f21664.png) 5 | 6 | ## Setup 7 | GitHub requires a personal access token to access its API. 8 | 9 | Follow the steps here to create your personal access token: https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/#creating-a-token. 10 | --------------------------------------------------------------------------------