Build projects API

Ten commit jest zawarty w:
Robert Marshall 2020-04-12 10:24:08 +01:00
commit 8c3760500f
49 zmienionych plików z 1416 dodań i 0 usunięć

51
.drone.yml Zwykły plik
Wyświetl plik

@ -0,0 +1,51 @@
kind: pipeline
name: default
clone:
skip_verify: true
volumes:
- name: output
host:
path: /var/www/Api.Projects
steps:
- name: build and publish
image: mcr.microsoft.com/dotnet/core/sdk:3.1
volumes:
- name: output
path: /output
environment:
GitApiEndpoint:
from_secret: GitApiEndpoint
GitApiToken:
from_secret: GitApiToken
commands:
- chmod +x ./build.sh
- ./build.sh
- sed -i "s/<GitApiEndpoint>/$GitApiEndpoint/g" output/appsettings.json
- sed -i "s/<GitApiToken>/$GitApiToken/g" output/appsettings.json
- cp api.projects.service output/
- cp -r ./output/* /output
- name: restart service
privileged: true
image: appleboy/drone-ssh
settings:
host: 192.168.1.3
username:
from_secret: ssh_user
password:
from_secret: ssh_password
script:
- systemctl daemon-reload
- service api.projects restart
- name: notify
image: drillster/drone-email
settings:
host: 192.168.1.3
skip_verify: true
from: build@robware.uk
when:
status:
- changed
- failure

198
.editorconfig Zwykły plik
Wyświetl plik

@ -0,0 +1,198 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
[*]
indent_style = tab
# C# files
[*.cs]
max_line_length = 180
# New line preferences
end_of_line = crlf
insert_final_newline = false
#### .NET Coding Conventions ####
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
# this. and Me. preferences
dotnet_style_qualification_for_event = false:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_property = false:silent
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
# Expression-level preferences
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_object_initializer = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
# Field preferences
dotnet_style_readonly_field = true:suggestion
# Parameter preferences
dotnet_code_quality_unused_parameters = all:suggestion
#### C# Coding Conventions ####
# var preferences
csharp_style_var_elsewhere = false:silent
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
# Expression-bodied members
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
# Null-checking preferences
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_prefer_static_local_function = true:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
# Code-block preferences
csharp_prefer_braces = false:silent
csharp_prefer_simple_using_statement = true:suggestion
# Expression-level preferences
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:silent
#### C# Formatting Rules ####
# New line preferences
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = none
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
[*.{yaml,yml}]
indent_style = space
indent_size = 2

1
.gitattributes dostarczono Zwykły plik
Wyświetl plik

@ -0,0 +1 @@
src/Robware.Api.Projects/appsettings.Development.json filter=clean-config

2
.gitconfig Zwykły plik
Wyświetl plik

@ -0,0 +1,2 @@
[filter "clean-config"]
clean = ./clean-config.sh

56
.gitignore dostarczono Zwykły plik
Wyświetl plik

@ -0,0 +1,56 @@
*.swp
*.*~
project.lock.json
.DS_Store
*.pyc
nupkg/
# Visual Studio Code
.vscode
# Rider
.idea
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
#build/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
msbuild.log
msbuild.err
msbuild.wrn
# Visual Studio 2015
.vs/
#lcov files
lcov.info
#compiled and minified assets
*.css
*.min.css
*.min.js
#nodejs
node_modules
output
.tmp
*.ncrunchsolution
/_NCrunch_Website
*.DotSettings
*.cache

1
.nuke Zwykły plik
Wyświetl plik

@ -0,0 +1 @@
src/Robware.Api.Projects.sln

9
Readme.md Zwykły plik
Wyświetl plik

@ -0,0 +1,9 @@
[![Build Status](https://build.robware.uk/api/badges/robware/Api.Projects/status.svg)](https://build.robware.uk/robware/Api.Projects)
# Projects microservice
Provides an API to retreive information from source control
## Setup
After clone, please run `git config --local include.path ../.gitconfig`. This will set up the filters required to ignore local dev config.

16
api.projects.service Zwykły plik
Wyświetl plik

@ -0,0 +1,16 @@
[Unit]
Description=Robware Projects API
[Service]
WorkingDirectory=/var/www/Api.Projects
ExecStart=/usr/bin/dotnet /var/www/Api.Projects/Robware.Api.Projects.dll
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=Api.Projects
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.target

68
build.ps1 Zwykły plik
Wyświetl plik

@ -0,0 +1,68 @@
[CmdletBinding()]
Param(
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$BuildArguments
)
Write-Output "Windows PowerShell $($Host.Version)"
Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { exit 1 }
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
###########################################################################
# CONFIGURATION
###########################################################################
$BuildProjectFile = "$PSScriptRoot\build\_build.csproj"
$TempDirectory = "$PSScriptRoot\\.tmp"
$DotNetGlobalFile = "$PSScriptRoot\\global.json"
$DotNetInstallUrl = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.ps1"
$DotNetChannel = "Current"
$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1
$env:DOTNET_CLI_TELEMETRY_OPTOUT = 1
###########################################################################
# EXECUTION
###########################################################################
function ExecSafe([scriptblock] $cmd) {
& $cmd
if ($LASTEXITCODE) { exit $LASTEXITCODE }
}
# If global.json exists, load expected version
if (Test-Path $DotNetGlobalFile) {
$DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json)
if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) {
$DotNetVersion = $DotNetGlobal.sdk.version
}
}
# If dotnet is installed locally, and expected version is not set or installation matches the expected version
if ((Get-Command "dotnet" -ErrorAction SilentlyContinue) -ne $null -and `
(!(Test-Path variable:DotNetVersion) -or $(& dotnet --version) -eq $DotNetVersion)) {
$env:DOTNET_EXE = (Get-Command "dotnet").Path
}
else {
$DotNetDirectory = "$TempDirectory\dotnet-win"
$env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
# Download install script
$DotNetInstallFile = "$TempDirectory\dotnet-install.ps1"
md -force $TempDirectory > $null
(New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile)
# Install by channel or version
if (!(Test-Path variable:DotNetVersion)) {
ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath }
} else {
ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
}
}
Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)"
ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false }
ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments }

62
build.sh Zwykły plik
Wyświetl plik

@ -0,0 +1,62 @@
#!/usr/bin/env bash
echo $(bash --version 2>&1 | head -n 1)
set -eo pipefail
SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
###########################################################################
# CONFIGURATION
###########################################################################
BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj"
TEMP_DIRECTORY="$SCRIPT_DIR//.tmp"
DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json"
DOTNET_INSTALL_URL="https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.sh"
DOTNET_CHANNEL="Current"
export DOTNET_CLI_TELEMETRY_OPTOUT=1
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
###########################################################################
# EXECUTION
###########################################################################
function FirstJsonValue {
perl -nle 'print $1 if m{"'$1'": "([^"\-]+)",?}' <<< ${@:2}
}
# If global.json exists, load expected version
if [ -f "$DOTNET_GLOBAL_FILE" ]; then
DOTNET_VERSION=$(FirstJsonValue "version" $(cat "$DOTNET_GLOBAL_FILE"))
if [ "$DOTNET_VERSION" == "" ]; then
unset DOTNET_VERSION
fi
fi
# If dotnet is installed locally, and expected version is not set or installation matches the expected version
if [[ -x "$(command -v dotnet)" && (-z ${DOTNET_VERSION+x} || $(dotnet --version) == "$DOTNET_VERSION") ]]; then
export DOTNET_EXE="$(command -v dotnet)"
else
DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix"
export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet"
# Download install script
DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh"
mkdir -p "$TEMP_DIRECTORY"
curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL"
chmod +x "$DOTNET_INSTALL_FILE"
# Install by channel or version
if [ -z ${DOTNET_VERSION+x} ]; then
"$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path
else
"$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path
fi
fi
echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)"
"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false
"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@"

10
build/.editorconfig Zwykły plik
Wyświetl plik

@ -0,0 +1,10 @@
[*.cs]
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:warning
dotnet_style_require_accessibility_modifiers = never:warning
csharp_style_expression_bodied_properties = true:warning
csharp_style_expression_bodied_indexers = true:warning
csharp_style_expression_bodied_accessors = true:warning

62
build/Build.cs Zwykły plik
Wyświetl plik

@ -0,0 +1,62 @@
using Nuke.Common;
using Nuke.Common.Execution;
using Nuke.Common.IO;
using Nuke.Common.ProjectModel;
using Nuke.Common.Tools.DotNet;
using Nuke.Common.Utilities.Collections;
using static Nuke.Common.IO.FileSystemTasks;
using static Nuke.Common.Tools.DotNet.DotNetTasks;
[CheckBuildProjectConfigurations]
[UnsetVisualStudioEnvironmentVariables]
class Build : NukeBuild {
public static int Main() => Execute<Build>(x => x.Publish);
[Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")]
readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release;
[Solution] readonly Solution Solution;
AbsolutePath SourceDirectory => RootDirectory / "src";
AbsolutePath OutputDirectory => RootDirectory / "output";
Target Clean => _ => _
.Before(Restore)
.Executes(() => {
SourceDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory);
EnsureCleanDirectory(OutputDirectory);
});
Target Restore => _ => _
.Executes(() => {
DotNetRestore(s => s
.SetProjectFile(Solution));
});
Target Compile => _ => _
.DependsOn(Restore)
.Executes(() => {
DotNetBuild(s => s
.SetProjectFile(Solution)
.SetConfiguration(Configuration)
.EnableNoRestore());
});
Target Test => _ => _
.DependsOn(Compile)
.Executes(() => {
DotNetTest(s => s
.SetProjectFile(Solution)
.EnableNoRestore());
});
Target Publish => _ => _
.DependsOn(Test)
.Executes(() => {
DotNetPublish(s => s
.SetProject(SourceDirectory / "Robware.Api.Projects/Robware.Api.Projects.csproj")
.SetConfiguration(Configuration)
.SetOutput(OutputDirectory));
});
}

34
build/_build.csproj Zwykły plik
Wyświetl plik

@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace></RootNamespace>
<IsPackable>False</IsPackable>
<NoWarn>CS0649;CS0169</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nuke.Common" Version="0.24.8" />
</ItemGroup>
<ItemGroup>
<NukeSpecificationFiles Include="**\*.json" Exclude="bin\**;obj\**" />
<NukeExternalFiles Include="**\*.*.ext" Exclude="bin\**;obj\**" />
<None Remove="*.csproj.DotSettings;*.ref.*.txt" />
<!-- Common build related files -->
<None Include="..\build.ps1" />
<None Include="..\build.sh" />
<None Include="..\.nuke" />
<None Include="..\global.json" Condition="Exists('..\global.json')" />
<None Include="..\nuget.config" Condition="Exists('..\nuget.config')" />
<None Include="..\azure-pipelines.yml" Condition="Exists('..\azure-pipelines.yml')" />
<None Include="..\Jenkinsfile" Condition="Exists('..\Jenkinsfile')" />
<None Include="..\appveyor.yml" Condition="Exists('..\appveyor.yml')" />
<None Include="..\.travis.yml" Condition="Exists('..\.travis.yml')" />
<None Include="..\GitVersion.yml" Condition="Exists('..\GitVersion.yml')" />
</ItemGroup>
</Project>

6
clean-config.sh Zwykły plik
Wyświetl plik

@ -0,0 +1,6 @@
#!/bin/sh
sed \
-e 's/"gitApiEndpoint": ".*"/"gitApiEndpoint": ""/g' \
-e 's/"gitApiToken": ".*"/"gitApiToken": ""/g' \
$1

Wyświetl plik

@ -0,0 +1,109 @@
using System;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using Robware.Api.Projects.Controllers;
using Robware.Projects.Code;
using Robware.Projects.Gogs;
using Xunit;
namespace Robware.Api.Projects.Tests {
public class CodeControllerTests {
private class TestRepository : Repository {
public TestRepository(string name, string url) {
Name = name;
Url = url;
}
}
private class TestBranch : Branch {
public TestBranch(string name, Commit commit) {
Name = name;
Commit = commit;
}
}
private class TestCommit : Commit {
public TestCommit(string id, string message, DateTimeOffset timestamp, string url = null) {
Id = id;
Message = message;
Timestamp = timestamp;
Url = url;
}
}
[Fact]
public async Task Repositories_WithUser_ReturnsRepositoryCollection() {
var logger = Substitute.For<ILogger<CodeController>>();
var api = Substitute.For<IGitApi>();
api.GetRepositories("test").Returns(new[] { new TestRepository("test", "url") });
var expectation = new[] { new TestRepository("test", "url") };
var controller = new CodeController(logger, api);
(await controller.Repositories("test")).Value.Should().BeEquivalentTo(expectation);
}
[Fact]
public async Task Repositories_WithUserThatDoesntExist_Returns404() {
var logger = Substitute.For<ILogger<CodeController>>();
var api = Substitute.For<IGitApi>();
api.GetRepositories("test").Throws(new UserNotFoundException("", null));
var controller = new CodeController(logger, api);
(await controller.Repositories("test")).Result.Should().BeOfType<NotFoundResult>();
}
[Fact]
public async Task Branches_WithUserAndRepository_ReturnsBranchCollection() {
var logger = Substitute.For<ILogger<CodeController>>();
var api = Substitute.For<IGitApi>();
api.GetBranches("user", "test").Returns(new[] {
new TestBranch("master", new TestCommit("0923b554309ef562fca978c7e981b3812bc4af40", "message", new DateTimeOffset(2020, 04, 11, 14, 09, 29, TimeSpan.FromHours(1))))
});
var expectation = new[] {
new TestBranch("master", new TestCommit("0923b554309ef562fca978c7e981b3812bc4af40", "message", new DateTimeOffset(2020, 04, 11, 14, 09, 29, TimeSpan.FromHours(1))))
};
var controller = new CodeController(logger, api);
(await controller.Branches("user", "test")).Value.Should().BeEquivalentTo(expectation);
}
[Fact]
public async Task Branches_WithUserOrRepositoryThatDoesntExist_Returns404() {
var logger = Substitute.For<ILogger<CodeController>>();
var api = Substitute.For<IGitApi>();
api.GetBranches("test", "404").Throws(new RepositoryNotFoundException("", "", null));
var controller = new CodeController(logger, api);
(await controller.Branches("test", "404")).Result.Should().BeOfType<NotFoundResult>();
}
[Fact]
public async Task Branches_WithUserAndRepositoryAndCommit_ReturnsBranchCollection() {
var logger = Substitute.For<ILogger<CodeController>>();
var api = Substitute.For<IGitApi>();
api.GetCommit("user", "test", "hash")
.Returns(new TestCommit("0923b554309ef562fca978c7e981b3812bc4af40", "message", new DateTimeOffset(2020, 04, 11, 14, 09, 29, TimeSpan.FromHours(1))));
var expectation = new TestCommit("0923b554309ef562fca978c7e981b3812bc4af40", "message", new DateTimeOffset(2020, 04, 11, 14, 09, 29, TimeSpan.FromHours(1)));
var controller = new CodeController(logger, api);
(await controller.Commit("user", "test", "hash")).Value.Should().BeEquivalentTo(expectation);
}
[Fact]
public async Task Branches_WithUserOrRepositoryOrCommitThatDoesntExist_Returns404() {
var logger = Substitute.For<ILogger<CodeController>>();
var api = Substitute.For<IGitApi>();
api.GetCommit("test", "404", "hash").Throws(new CommitNotFoundException("", "", "", null));
var controller = new CodeController(logger, api);
(await controller.Commit("test", "404", "hash")).Result.Should().BeOfType<NotFoundResult>();
}
}
}

Wyświetl plik

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="NSubstitute" Version="4.2.1" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Robware.Api.Projects\Robware.Api.Projects.csproj" />
<ProjectReference Include="..\Robware.Projects\Robware.Projects.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Logging.Abstractions">
<HintPath>C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\3.1.0\ref\netcoreapp3.1\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

Wyświetl plik

@ -0,0 +1,53 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Robware.Api.Projects", "Robware.Api.Projects\Robware.Api.Projects.csproj", "{DD0C5A40-8E42-4046-A36E-D6A4F9291900}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Robware.Api.Projects.Tests", "Robware.Api.Projects.Tests\Robware.Api.Projects.Tests.csproj", "{6CEB61CD-19B5-4104-BA77-D24980E03400}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robware.Projects", "Robware.Projects\Robware.Projects.csproj", "{A380C8FB-20EB-41D7-9310-9B33BDCB6A0F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robware.Projects.Gogs", "Robware.Projects.Gogs\Robware.Projects.Gogs.csproj", "{FEF1E250-4F42-4335-9462-9606F4B358CB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robware.Projects.Gogs.Tests", "Robware.Projects.Gogs.Tests\Robware.Projects.Gogs.Tests.csproj", "{5A6BB452-6F2A-44F7-BF5A-514C95608F2D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "..\build\_build.csproj", "{06BF28D8-AAE9-4727-BF4F-198AFF52AEBF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{06BF28D8-AAE9-4727-BF4F-198AFF52AEBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{06BF28D8-AAE9-4727-BF4F-198AFF52AEBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD0C5A40-8E42-4046-A36E-D6A4F9291900}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD0C5A40-8E42-4046-A36E-D6A4F9291900}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD0C5A40-8E42-4046-A36E-D6A4F9291900}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD0C5A40-8E42-4046-A36E-D6A4F9291900}.Release|Any CPU.Build.0 = Release|Any CPU
{6CEB61CD-19B5-4104-BA77-D24980E03400}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CEB61CD-19B5-4104-BA77-D24980E03400}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CEB61CD-19B5-4104-BA77-D24980E03400}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CEB61CD-19B5-4104-BA77-D24980E03400}.Release|Any CPU.Build.0 = Release|Any CPU
{A380C8FB-20EB-41D7-9310-9B33BDCB6A0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A380C8FB-20EB-41D7-9310-9B33BDCB6A0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A380C8FB-20EB-41D7-9310-9B33BDCB6A0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A380C8FB-20EB-41D7-9310-9B33BDCB6A0F}.Release|Any CPU.Build.0 = Release|Any CPU
{FEF1E250-4F42-4335-9462-9606F4B358CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FEF1E250-4F42-4335-9462-9606F4B358CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FEF1E250-4F42-4335-9462-9606F4B358CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FEF1E250-4F42-4335-9462-9606F4B358CB}.Release|Any CPU.Build.0 = Release|Any CPU
{5A6BB452-6F2A-44F7-BF5A-514C95608F2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5A6BB452-6F2A-44F7-BF5A-514C95608F2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5A6BB452-6F2A-44F7-BF5A-514C95608F2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5A6BB452-6F2A-44F7-BF5A-514C95608F2D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F9052B29-4197-4FFC-A5D7-C4DFDF0DD5BD}
EndGlobalSection
EndGlobal

Wyświetl plik

@ -0,0 +1,40 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Robware.Projects.Code;
using Robware.Projects.Gogs;
namespace Robware.Api.Projects.Controllers {
[ApiController]
[Route("[controller]")]
public class CodeController : ControllerBase {
private readonly ILogger<CodeController> _logger;
private readonly IGitApi _gitApi;
public CodeController(ILogger<CodeController> logger, IGitApi gitApi) {
_logger = logger;
_gitApi = gitApi;
}
private async Task<ActionResult<TResult>> Do<TResult, TException>(Func<Task<TResult>> method) where TException : Exception {
try {
return await method();
}
catch (TException e) {
_logger.Log(LogLevel.Error, e.Message);
return NotFound();
}
}
[HttpGet(nameof(Repositories))]
public async Task<ActionResult<Repository[]>> Repositories(string user) => await Do<Repository[], UserNotFoundException>(async ()=> (await _gitApi.GetRepositories(user)).ToArray());
[HttpGet(nameof(Branches))]
public async Task<ActionResult<Branch[]>> Branches(string user, string repository) => await Do<Branch[], RepositoryNotFoundException>(async() => (await _gitApi.GetBranches(user, repository)).ToArray());
[HttpGet(nameof(Commit))]
public async Task<ActionResult<Commit>> Commit(string user, string repository, string hash) => await Do<Commit, CommitNotFoundException>(async () => await _gitApi.GetCommit(user, repository, hash));
}
}

Wyświetl plik

@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Robware.Api.Projects {
public class Program {
public static void Main(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => {
webBuilder.UseStartup<Startup>();
})
.Build()
.Run();
}
}

Wyświetl plik

@ -0,0 +1,28 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:63705",
"sslPort": 44380
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Robware.Api.Projects": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
}

Wyświetl plik

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Robware.Projects.Gogs\Robware.Projects.Gogs.csproj" />
<ProjectReference Include="..\Robware.Projects\Robware.Projects.csproj" />
</ItemGroup>
</Project>

Wyświetl plik

@ -0,0 +1,46 @@
using System;
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Robware.Projects.Code;
using Robware.Projects.Gogs;
namespace Robware.Api.Projects {
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
services.AddControllers();
services.AddHttpClient<IGitApi, GogsApi>(client => {
client.BaseAddress = new Uri(Configuration["gitApiEndpoint"]);
client.DefaultRequestHeaders.Add("Authorization", $"token {Configuration["gitApiToken"]}");
})
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler {ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
});
}
}
}

Wyświetl plik

@ -0,0 +1,11 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"gitApiEndpoint": "",
"gitApiToken": ""
}

Wyświetl plik

@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"gitApiEndpoint": "<GitApiEndpoint>",
"gitApiToken": "<GitApiToken>"
}

Wyświetl plik

@ -0,0 +1,114 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Robware.Projects.Code;
using Xunit;
namespace Robware.Projects.Gogs.Tests {
public class GogsApiTests {
private class TestRepository : Repository {
public TestRepository(string name, string url) {
Name = name;
Url = url;
}
}
private class TestBranch : Branch {
public TestBranch(string name, Commit commit) {
Name = name;
Commit = commit;
}
}
private class TestCommit : Commit {
public TestCommit(string id, string message, DateTimeOffset timestamp, string url = null) {
Id = id;
Message = message;
Timestamp = timestamp;
Url = url;
}
}
[Fact]
public async Task GetRepositories_WithUser_ReturnsRepositoriesCollection() {
const string json = @"[{""id"":1,""owner"":{""id"":1,""username"":""test"",""login"":""test"",""full_name"":""Test User"",""email"":""test@test.com"",""avatar_url"":""https://test.com/avatars/1""},""name"":""Repo Name"",""full_name"":""test/repo"",""description"":"""",""private"":false,""fork"":false,""parent"":null,""empty"":false,""mirror"":false,""size"":5648384,""html_url"":""https://test.com/test/repo"",""ssh_url"":""git@test.com:test/repo.git"",""clone_url"":""https://test.com/test/repo.git"",""website"":"""",""stars_count"":0,""forks_count"":0,""watchers_count"":1,""open_issues_count"":0,""default_branch"":""master"",""created_at"":""2019-04-18T14:33:05+01:00"",""updated_at"":""2020-04-11T14:09:33+01:00"",""permissions"":{""admin"":true,""push"":true,""pull"":true}}]";
var httpClient = new HttpClientBuilder()
.WithMethod(HttpMethod.Get)
.WithUrl("/api/v1/users/test/repos")
.WithResponse(json)
.Build();
var api = new GogsApi(httpClient);
var expectation = new[] { new TestRepository("Repo Name", "https://test.com/test/repo") };
(await api.GetRepositories("test")).Should().BeEquivalentTo(expectation);
}
[Fact]
public void GetRepositories_WithUserThatDoesntExist_ThrowsUserNotFoundException() {
var httpClient = new HttpClientBuilder()
.WithMethod(HttpMethod.Get)
.WithUrl("/api/v1/users/test/repos")
.WithErrorStatus(HttpStatusCode.NotFound)
.Build();
var api = new GogsApi(httpClient);
api.Invoking(gogsApi => gogsApi.GetRepositories("404")).Should().Throw<UserNotFoundException>().WithMessage("Could not find user 404");
}
[Fact]
public async Task GetBranches_WithUserAndRepository_ReturnsBranchCollection() {
const string json = @"[{""name"":""master"",""commit"":{""id"":""0923b554309ef562fca978c7e981b3812bc4af40"",""message"":""message"",""url"":""Not implemented"",""author"":{""name"":""Test User"",""email"":""test@test.com"",""username"":""test""},""committer"":{""name"":""Test User"",""email"":""test@test.com"",""username"":""test""},""added"":null,""removed"":null,""modified"":null,""timestamp"":""2020-04-11T14:09:29+01:00""}}]";
var httpClient = new HttpClientBuilder()
.WithMethod(HttpMethod.Get)
.WithUrl("/api/v1/repos/user/test/branches")
.WithResponse(json)
.Build();
var api = new GogsApi(httpClient);
var expectation = new[] {
new TestBranch("master", new TestCommit("0923b554309ef562fca978c7e981b3812bc4af40", "message", new DateTimeOffset(2020, 04, 11, 14, 09, 29, TimeSpan.FromHours(1))))
};
(await api.GetBranches("user", "test")).Should().BeEquivalentTo(expectation);
}
[Fact]
public void GetBranches_WithUserOrRepositoryThatDoesntExist_ThrowsRepositoryNotFoundException() {
var httpClient = new HttpClientBuilder()
.WithMethod(HttpMethod.Get)
.WithUrl("/api/v1/repos/user/test/branches")
.WithErrorStatus(HttpStatusCode.NotFound)
.Build();
var api = new GogsApi(httpClient);
api.Invoking(gogsApi => gogsApi.GetBranches("test", "404")).Should().Throw<RepositoryNotFoundException>().WithMessage("Could not find repository 404 for user test");
}
[Fact]
public async Task GetCommit_WithUserAndRepositoryAndCommitHash_ReturnsCommit() {
const string json = @"{""url"":""https://test.com/api/v1/repos/user/repo/commits/0923b554309ef562fca978c7e981b3812bc4af40"",""sha"":""0923b554309ef562fca978c7e981b3812bc4af40"",""html_url"":""https://test.com/user/repo/commits/0923b554309ef562fca978c7e981b3812bc4af40"",""commit"":{""url"":""https://test.com/api/v1/repos/user/repo/commits/0923b554309ef562fca978c7e981b3812bc4af40"",""author"":{""name"":""Test User"",""email"":""test@test.com"",""date"":""2020-04-11T14:09:29+01:00""},""committer"":{""name"":""Test User"",""email"":""test@test.com"",""date"":""2020-04-11T14:09:29+01:00""},""message"":""message"",""tree"":{""url"":""https://test.com/api/v1/repos/user/repo/tree/0923b554309ef562fca978c7e981b3812bc4af40"",""sha"":""0923b554309ef562fca978c7e981b3812bc4af40""}},""author"":{""id"":1,""username"":""test"",""login"":""test"",""full_name"":""Test User"",""email"":""test@test.com"",""avatar_url"":""https://test.com/avatars/1""},""committer"":{""id"":1,""username"":""test"",""login"":""test"",""full_name"":""Test User"",""email"":""test@test.com"",""avatar_url"":""https://test.com/avatars/1""},""parents"":[{""url"":""https://test.com/api/v1/repos/user/repo/commits/25c320bf6be45262e49650a5a86bb71506d29617"",""sha"":""25c320bf6be45262e49650a5a86bb71506d29617""}]}";
var httpClient = new HttpClientBuilder()
.WithMethod(HttpMethod.Get)
.WithUrl("/api/v1/repos/user/repo/commits/test")
.WithResponse(json)
.Build();
var api = new GogsApi(httpClient);
var expectation = new TestCommit("0923b554309ef562fca978c7e981b3812bc4af40", "message", new DateTimeOffset(2020, 04, 11, 14, 09, 29, TimeSpan.FromHours(1)), "https://test.com/user/repo/commit/0923b554309ef562fca978c7e981b3812bc4af40");
(await api.GetCommit("user", "repo", "test")).Should().BeEquivalentTo(expectation);
}
[Fact]
public void GetCommit_WithUserOrRepositoryOrCommitHashThatDoesntExist_ThrowsCommitNotFoundException() {
var httpClient = new HttpClientBuilder()
.WithMethod(HttpMethod.Get)
.WithUrl("/api/v1/repos/user/repo/commits/test")
.WithErrorStatus(HttpStatusCode.NotFound)
.Build();
var api = new GogsApi(httpClient);
api.Invoking(gogsApi => gogsApi.GetCommit("test", "404", "hash")).Should().Throw<CommitNotFoundException>().WithMessage("Could not find commit hash for user test in repository 404");
}
}
}

Wyświetl plik

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using RichardSzalay.MockHttp;
namespace Robware.Projects.Gogs.Tests {
public class HttpClientBuilder {
private string _url, _response, _body;
private HttpMethod _method;
private readonly Dictionary<string, string> _queries = new Dictionary<string, string>();
private HttpStatusCode _fallbackCode = HttpStatusCode.NotImplemented;
public HttpClientBuilder WithUrl(string url) {
_url = url;
return this;
}
public HttpClientBuilder WithResponse(string response) {
_response = response;
return this;
}
public HttpClientBuilder WithMethod(HttpMethod method) {
_method = method;
return this;
}
public HttpClientBuilder WithQueryString(string key, string value) {
_queries.Add(key, value);
return this;
}
public HttpClientBuilder WithPostBody(string body) {
_body = body;
return this;
}
public HttpClientBuilder WithErrorStatus(HttpStatusCode statusCode) {
_fallbackCode = statusCode;
return this;
}
public HttpClient Build(out MockHttpMessageHandler mockHttpMessageHandler) {
mockHttpMessageHandler = new MockHttpMessageHandler();
mockHttpMessageHandler.Fallback.Respond(_fallbackCode, message => message.Content = new StringContent(string.Empty));
var mockedRequest = mockHttpMessageHandler.Expect(_method, _url).Respond("application/json", _response ?? "penis");
if (_queries.Any())
mockedRequest.WithExactQueryString(_queries);
if (!string.IsNullOrEmpty(_body) || _method == HttpMethod.Post)
mockedRequest.WithContent(_body ?? string.Empty);
var httpClient = mockHttpMessageHandler.ToHttpClient();
httpClient.BaseAddress = new Uri("http://example.com/");
return httpClient;
}
public HttpClient Build() => Build(out _);
}
}

Wyświetl plik

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Robware.Projects.Gogs\Robware.Projects.Gogs.csproj" />
</ItemGroup>
</Project>

Wyświetl plik

@ -0,0 +1,9 @@
using System;
namespace Robware.Projects.Gogs {
public class CommitNotFoundException : Exception {
public CommitNotFoundException(string user, string repository, string hash, Exception innerException) :base ($"Could not find commit {hash} for user {user} in repository {repository}", innerException) {
}
}
}

Wyświetl plik

@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Robware.Projects.Code;
using Robware.Projects.Gogs.Models;
using Robware.Projects.Gogs.States;
namespace Robware.Projects.Gogs {
public class GogsApi : IGitApi {
private readonly HttpClient _client;
public GogsApi(HttpClient client) {
_client = client;
}
private async Task<T> Get<T>(string url) {
var response = await _client.GetStringAsync(url);
return JsonConvert.DeserializeObject<T>(response);
}
public async Task<IEnumerable<Repository>> GetRepositories(string user) {
try {
return (await Get<IEnumerable<RepositoryState>>($"api/v1/users/{user}/repos")).Select(state => new GogsRepository(state));
}
catch (HttpRequestException e) {
throw new UserNotFoundException(user, e);
}
}
public async Task<IEnumerable<Branch>> GetBranches(string user, string repository) {
try {
return (await Get<IEnumerable<BranchState>>($"api/v1/repos/{user}/{repository}/branches")).Select(state => new GogsBranch(state));
}
catch (HttpRequestException e) {
throw new RepositoryNotFoundException(user, repository, e);
}
}
public async Task<Commit> GetCommit(string user, string repository, string hash) {
try {
return new GogsCommit(await Get<CommitDetailsState>($"api/v1/repos/{user}/{repository}/commits/{hash}"));
}
catch (HttpRequestException e) {
throw new CommitNotFoundException(user, repository, hash, e);
}
}
}
}

Wyświetl plik

@ -0,0 +1,12 @@
using Robware.Projects.Code;
using Robware.Projects.Gogs.States;
namespace Robware.Projects.Gogs.Models {
public class GogsBranch : Branch {
public GogsBranch(BranchState state) {
Name = state.name;
Commit = new GogsCommit(state.commit);
}
}
}

Wyświetl plik

@ -0,0 +1,19 @@
using Robware.Projects.Code;
using Robware.Projects.Gogs.States;
namespace Robware.Projects.Gogs.Models {
public class GogsCommit : Commit {
public GogsCommit(CommitState commit) {
Id = commit.id;
Message = commit.message;
Timestamp = commit.timestamp;
}
public GogsCommit(CommitDetailsState commit) {
Id = commit.sha;
Message = commit.commit.message;
Timestamp = commit.commit.author.date;
Url = commit.html_url.Replace("/commits/", "/commit/");
}
}
}

Wyświetl plik

@ -0,0 +1,11 @@
using Robware.Projects.Code;
using Robware.Projects.Gogs.States;
namespace Robware.Projects.Gogs.Models {
public class GogsRepository : Repository {
public GogsRepository(RepositoryState state) {
Name = state.name;
Url = state.html_url;
}
}
}

Wyświetl plik

@ -0,0 +1,9 @@
using System;
namespace Robware.Projects.Gogs {
public class RepositoryNotFoundException : Exception {
public RepositoryNotFoundException(string user, string repository, Exception innerException) : base($"Could not find repository {repository} for user {user}", innerException) {
}
}
}

Wyświetl plik

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="States\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Robware.Projects\Robware.Projects.csproj" />
</ItemGroup>
</Project>

Wyświetl plik

@ -0,0 +1,10 @@
using System;
namespace Robware.Projects.Gogs.States {
public class AuthorState {
public string name { get; set; }
public string email { get; set; }
public string username { get; set; }
public DateTimeOffset date { get; set; }
}
}

Wyświetl plik

@ -0,0 +1,7 @@
namespace Robware.Projects.Gogs.States {
public class BranchState
{
public string name { get; set; }
public CommitState commit { get; set; }
}
}

Wyświetl plik

@ -0,0 +1,11 @@
namespace Robware.Projects.Gogs.States {
public class CommitDetailsState {
public string url { get; set; }
public string sha { get; set; }
public string html_url { get; set; }
public CommitState commit { get; set; }
public UserState author { get; set; }
public UserState committer { get; set; }
public CommitPointerState[] parents { get; set; }
}
}

Wyświetl plik

@ -0,0 +1,6 @@
namespace Robware.Projects.Gogs.States {
public partial class CommitPointerState {
public string url { get; set; }
public string sha { get; set; }
}
}

Wyświetl plik

@ -0,0 +1,16 @@
using System;
namespace Robware.Projects.Gogs.States {
public class CommitState {
public string id { get; set; }
public string message { get; set; }
public string url { get; set; }
public AuthorState author { get; set; }
public AuthorState committer { get; set; }
public object added { get; set; }
public object removed { get; set; }
public object modified { get; set; }
public DateTimeOffset timestamp { get; set; }
public CommitPointerState tree { get; set; }
}
}

Wyświetl plik

@ -0,0 +1,10 @@
namespace Robware.Projects.Gogs.States {
public class RepositoryOwnerState {
public int id { get; set; }
public string username { get; set; }
public string login { get; set; }
public string full_name { get; set; }
public string email { get; set; }
public string avatar_url { get; set; }
}
}

Wyświetl plik

@ -0,0 +1,7 @@
namespace Robware.Projects.Gogs.States {
public class RepositoryPermissionsState {
public bool admin { get; set; }
public bool push { get; set; }
public bool pull { get; set; }
}
}

Wyświetl plik

@ -0,0 +1,31 @@
using System;
namespace Robware.Projects.Gogs.States {
public class RepositoryState {
public int id { get; set; }
public RepositoryOwnerState owner { get; set; }
public string name { get; set; }
public string full_name { get; set; }
public string description { get; set; }
public bool _private { get; set; }
public bool fork { get; set; }
public object parent { get; set; }
public bool empty { get; set; }
public bool mirror { get; set; }
public int size { get; set; }
public string html_url { get; set; }
public string ssh_url { get; set; }
public string clone_url { get; set; }
public string website { get; set; }
public int stars_count { get; set; }
public int forks_count { get; set; }
public int watchers_count { get; set; }
public int open_issues_count { get; set; }
public string default_branch { get; set; }
public DateTime created_at { get; set; }
public DateTime updated_at { get; set; }
public RepositoryPermissionsState permissions { get; set; }
}
}

Wyświetl plik

@ -0,0 +1,10 @@
namespace Robware.Projects.Gogs.States {
public partial class UserState {
public long id { get; set; }
public string username { get; set; }
public string login { get; set; }
public string full_name { get; set; }
public string email { get; set; }
public string avatar_url { get; set; }
}
}

Wyświetl plik

@ -0,0 +1,9 @@
using System;
namespace Robware.Projects.Gogs {
public class UserNotFoundException : Exception {
public UserNotFoundException(string user, Exception innerException) : base($"Could not find user {user}", innerException) {
}
}
}

Wyświetl plik

@ -0,0 +1,6 @@
namespace Robware.Projects.Code {
public abstract class Branch {
public string Name { get; protected set; }
public Commit Commit { get; protected set; }
}
}

Wyświetl plik

@ -0,0 +1,10 @@
using System;
namespace Robware.Projects.Code {
public abstract class Commit {
public string Id { get; protected set; }
public string Message { get; protected set; }
public DateTimeOffset Timestamp { get; protected set; }
public string Url { get; protected set; }
}
}

Wyświetl plik

@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Robware.Projects.Code {
public interface IGitApi {
Task<IEnumerable<Repository>> GetRepositories(string user);
Task<IEnumerable<Branch>> GetBranches(string user, string repository);
Task<Commit> GetCommit(string user, string repository, string hash);
}
}

Wyświetl plik

@ -0,0 +1,6 @@
namespace Robware.Projects.Code {
public abstract class Repository {
public string Name { get; protected set; }
public string Url { get; protected set; }
}
}

Wyświetl plik

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
</Project>