Skip to content

Commit

Permalink
Merge pull request #1017 from imgbot/feat/count_private_repository_plan
Browse files Browse the repository at this point in the history
Feat/count private repository plan
  • Loading branch information
GrigoreMihai authored Oct 19, 2021
2 parents 4d5e7bb + 8b0784a commit 1411d56
Show file tree
Hide file tree
Showing 25 changed files with 647 additions and 8,681 deletions.
9 changes: 6 additions & 3 deletions Auth/AuthFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ public static async Task<HttpResponseMessage> Callback(
var mktplcResponse = await HttpClient.SendAsync(mktplcRequest);
var planDataJson = await mktplcResponse.Content.ReadAsStringAsync();
var planData = JsonConvert.DeserializeObject<PlanData[]>(planDataJson);

var eduData = new Edu();
var isStudent = false;
try
{
var eduRequest = new HttpRequestMessage(HttpMethod.Get, "https://education.github.com/api/user");
Expand All @@ -110,6 +110,10 @@ public static async Task<HttpResponseMessage> Callback(
var eduResponse = await HttpClient.SendAsync(eduRequest);
var eduDataJson = await eduResponse.Content.ReadAsStringAsync();
eduData = JsonConvert.DeserializeObject<Edu>(eduDataJson);
if (eduData != null)
{
isStudent = eduData.Student;
}
}
catch (Exception e)
{
Expand All @@ -122,9 +126,8 @@ public static async Task<HttpResponseMessage> Callback(
{
AccountType = item.account.type,
PlanId = item.plan.id,
Student = eduData.Student,
Student = isStudent,
};

await marketplaceTable.CreateIfNotExistsAsync();
await marketplaceTable.ExecuteAsync(TableOperation.InsertOrMerge(marketplaceRow));
}
Expand Down
65 changes: 60 additions & 5 deletions Auth/InstallationFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public static async Task<HttpResponseMessage> ListAsync(
x.account.avatar_url,
planId = (mktplc.Result as Common.TableModels.Marketplace)?.PlanId,
student = (mktplc.Result as Common.TableModels.Marketplace)?.Student,
allowedPrivate = (mktplc.Result as Common.TableModels.Marketplace)?.AllowedPrivate,
usedPrivate = (mktplc.Result as Common.TableModels.Marketplace)?.UsedPrivate,
};
}));

Expand Down Expand Up @@ -167,9 +169,10 @@ public static async Task<HttpResponseMessage> ListPullsAsync(

[FunctionName("RequestRepositoryCheckFunction")]
public static async Task<HttpResponseMessage> RequestRepositoryCheckAsync(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "repositories/check/{installationid}/{repositoryid}")]HttpRequestMessage req,
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "repositories/check/{installationid}/{repositoryid}/{compress?}")]HttpRequestMessage req,
string installationid,
string repositoryid)
string repositoryid,
string compress)
{
var token = req.ReadCookie("token");
if (token == null)
Expand All @@ -190,22 +193,71 @@ public static async Task<HttpResponseMessage> RequestRepositoryCheckAsync(
response.EnableCors();

var imgbotBranch = await GetImgbotBranch(repository, token);
if (imgbotBranch != null)
if (imgbotBranch != null && compress == null)
{
// branch already exists
response.SetJson(new { status = "branchexists" });
return response;
}

var shouldCompress = true;
if (compress == "false")
{
shouldCompress = false;
}

bool updateValid = true;
int? usedPrivateValue = 0;
if (compress != null && repository.@private == true)
{
updateValid = false;
var marketplaceTable = GetTable("marketplace");
var installationsData = await GetInstallationsData(token);
await Task.WhenAll(installationsData.installations.Select(async x =>
{
var mktplc = await marketplaceTable.ExecuteAsync(
TableOperation.Retrieve<Common.TableModels.Marketplace>(x.account.id.ToString(), x.account.login));

var allowedPrivate = (mktplc.Result as Common.TableModels.Marketplace)?.AllowedPrivate;
var usedPrivate = (mktplc.Result as Common.TableModels.Marketplace)?.UsedPrivate;
usedPrivateValue = usedPrivate;
if (shouldCompress)
{
if (usedPrivate < allowedPrivate || allowedPrivate == null)
{
usedPrivate++;
updateValid = true;
}
}
else
{
usedPrivate--;
updateValid = true;
}

if (updateValid == true)
{
await marketplaceTable.ExecuteAsync(TableOperation.InsertOrMerge(new Common.TableModels.Marketplace(x.account.id, x.account.login)
{
UsedPrivate = usedPrivate,
}));
usedPrivateValue = usedPrivate;
}
}));
}

await routerQueue.AddMessageAsync(new CloudQueueMessage(JsonConvert.SerializeObject(new Common.Messages.RouterMessage
{
CloneUrl = repository.html_url,
InstallationId = Convert.ToInt32(installationid),
Owner = repository.owner.login,
RepoName = repository.name,
Compress = shouldCompress,
IsPrivate = repository.@private,
Update = updateValid,
})));

response.SetJson(new { status = "OK" });
response.SetJson(new { status = "OK", usedPrivate = usedPrivateValue });
return response;
}

Expand All @@ -222,6 +274,7 @@ public static async Task<HttpResponseMessage> GetRepositorySettingsAsync(
}

var repository = await GetRepository(installationid, token, repositoryid);

if (repository == null)
{
throw new Exception("repository request mismatch");
Expand Down Expand Up @@ -308,7 +361,9 @@ private static async Task<object> RepositoryResponse(Model.Repository ghReposito
ghRepository.html_url,
ghRepository.name,
ghRepository.fork,
lastchecked = (installation.Result as Common.TableModels.Installation)?.LastChecked
lastchecked = (installation.Result as Common.TableModels.Installation)?.LastChecked,
IsPrivate = (installation.Result as Common.TableModels.Installation)?.IsPrivate,
IsOptimized = (installation.Result as Common.TableModels.Installation)?.IsOptimized
};
}

Expand Down
34 changes: 30 additions & 4 deletions Common/KnownGitHubs.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace Common
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Common
{
public static class KnownGitHubs
{
Expand All @@ -14,9 +17,32 @@ public static class KnownGitHubs

public const string BranchName = "imgbot";

/// <remarks>
/// {0} = installation_id
/// </remarks>
// <remarks>
// {0} = installation_id.
// </remarks>
public const string AccessTokensUrlFormat = "https://api.github.com/app/installations/{0}/access_tokens";

public const int SmallestLimitPaidPlan = 5;

// -1 for existing plans that include unlimited private
// -2 for old plans which also have unlimited private, that do not need marketplacesync
// int values represent the number of private repos
// the last plan we need to edit after creating it in github market place
public static readonly ReadOnlyDictionary<int, int> Plans
= new ReadOnlyDictionary<int, int>(
new Dictionary<int, int>()
{
{ 781, -2 },
{ 1749, 0 },
{ 1750, -2 },
{ 2840, -1 },
{ 2841, -1 },
{ 6894, 5 },
{ 6919, 10 },
{ 6920, 20 },
{ 6921, 50 },
{ 6922, 100 },
{ 6923, 200 },
});
}
}
6 changes: 6 additions & 0 deletions Common/Messages/RouterMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,11 @@ public class RouterMessage
public string Owner { get; set; }

public bool IsRebase { get; set; }

public bool IsPrivate { get; set; }

public bool Compress { get; set; }

public bool Update { get; set; }
}
}
6 changes: 6 additions & 0 deletions Common/RepoConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,11 @@ public class RepoConfiguration
public bool CompressWiki { get; set; }

public int? MinKBReduced { get; set; } = 10;

public string PrTitle { get; set; }

public string PrBody { get; set; }

// public string Labels { get; set; } TODO: add when having the labels feature
}
}
4 changes: 4 additions & 0 deletions Common/TableModels/Installation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,9 @@ public Installation(int installationId, string repoName)
public string Owner { get; set; }

public DateTime? LastChecked { get; set; }

public bool IsPrivate { get; set; }

public bool IsOptimized { get; set; }
}
}
10 changes: 7 additions & 3 deletions Common/TableModels/Marketplace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ public Marketplace(int accountId, string accountLogin)

public string OrganizationBillingEmail { get; set; }

public int PlanId { get; set; }
public int? PlanId { get; set; }

public int SenderId { get; set; }
public int? SenderId { get; set; }

public string SenderLogin { get; set; }

public bool Student { get; set; }
public bool? Student { get; set; }

public int? AllowedPrivate { get; set; }

public int? UsedPrivate { get; set; }
}
}
6 changes: 6 additions & 0 deletions Common/TableModels/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,11 @@ public Settings(string installationId, string repoName)
public string RepoName { get; set; }

public string DefaultBranchOverride { get; set; }

public string PrBody { get; set; }

public string PrTitle { get; set; }

public string Labels { get; set; }
}
}
54 changes: 54 additions & 0 deletions CompressImagesFunction/CompressImages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
using System.Threading.Tasks;
using Common;
using Common.Messages;
using Common.TableModels;
using CompressImagesFunction.Compressors;
using ImageMagick;
using LibGit2Sharp;
using LibGit2Sharp.Handlers;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using Newtonsoft.Json;

namespace CompressImagesFunction
Expand All @@ -28,6 +31,8 @@ public static class CompressImages

public static bool Run(CompressimagesParameters parameters, ICollector<CompressImagesMessage> compressImagesMessages, ILogger logger)
{
var storageAccount = CloudStorageAccount.Parse(Common.KnownEnvironmentVariables.AzureWebJobsStorage);
var paidPlan = PaidPlan(storageAccount, parameters.RepoOwner);
CredentialsHandler credentialsProvider =
(url, user, cred) =>
new UsernamePasswordCredentials { Username = KnownGitHubs.Username, Password = parameters.Password };
Expand Down Expand Up @@ -97,6 +102,23 @@ public static bool Run(CompressimagesParameters parameters, ICollector<CompressI
if (!string.IsNullOrEmpty(repoConfigJson))
{
repoConfiguration = JsonConvert.DeserializeObject<RepoConfiguration>(repoConfigJson);

// for now we are not adding the labels functionality || repoConfiguration.Labels.Any() TODO: add it when adding the labels feature
if (paidPlan && (repoConfiguration.PrBody != null || repoConfiguration.PrTitle != null))
{
var settingsTable = storageAccount.CreateCloudTableClient().GetTableReference("settings");

// Labels = repoConfiguration.Labels TODO: add it when adding the labels feature
var settings = new Common.TableModels.Settings(
parameters.CompressImagesMessage.InstallationId.ToString(),
parameters.CompressImagesMessage.RepoName)
{
PrBody = repoConfiguration.PrBody,
PrTitle = repoConfiguration.PrTitle,
};

settingsTable.ExecuteAsync(TableOperation.InsertOrReplace(settings)).Wait();
}
}
}
catch
Expand Down Expand Up @@ -366,5 +388,37 @@ private static CompressionResult[] OptimizeImages(Repository repo, string localP
logger.LogInformation("Compressed {NumImages}", optimizedImages.Count);
return optimizedImages.ToArray();
}

private static bool PaidPlan(CloudStorageAccount storageAccount, string ownerLogin)
{
var marketplaceTable = storageAccount.CreateCloudTableClient().GetTableReference("marketplace");
var paidPlans = KnownGitHubs.Plans.Keys.Where(k => KnownGitHubs.Plans[k] != 0);
string plansQuery = string.Empty;
string needsOr = string.Empty;
if (paidPlans.Count() > 0)
{
needsOr = " or ";
}

int i = 0;
foreach (int planId in paidPlans)
{
plansQuery += "PlanId eq " + planId.ToString();
if (i != paidPlans.Count() - 1)
{
plansQuery += needsOr;
}

i++;
}

var query = new TableQuery<Marketplace>().Where(
$"AccountLogin eq '{ownerLogin}' and ({plansQuery})");

var rows = marketplaceTable.ExecuteQuerySegmentedAsync(query, null).Result;
var plan = rows.FirstOrDefault();

return plan != null;
}
}
}
5 changes: 4 additions & 1 deletion Docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ Here is an example .imgbotconfig setup that shows some of the options.
],
"aggressiveCompression": "true", // true|false
"compressWiki": "true", // true|false
"minKBReduced": 500 // delay new prs until size reduction meets this threshold (default to 10)
"minKBReduced": 500, // delay new prs until size reduction meets this threshold (default to 10)
"prTitle" : "Your own pr title",
"prBody" : " Text before optimization ratio {optimization_ratio} Text after optimization ratio
Text before optimization details {optimization_details} Text after optimization details",
}
```

Expand Down
15 changes: 15 additions & 0 deletions Docs/pr-body.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
You can set a pull request body for your Imgbot PRs through the `.imgbotconfig` file.

- This configuration is optional and is only required if you want a custom body for your pr's
- Available only for paid plans
- Accepts any string written using github [markdown](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax)
- The default pr body to display is the one from [here](https://imgbot.net/images/screen.png?cache=2)

`.imgbotconfig`

```
{
"prBody" : " Text before optimization ratio {optimization_ratio} Text after optimization ratio
Text before optimization details {optimization_details} Text after optimization details",
}
```
14 changes: 14 additions & 0 deletions Docs/pr-title.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
You can set a pull request title for your Imgbot PRs through the `.imgbotconfig` file.

- This configuration is optional and is only required if you want a custom title for your pr's
- Available only for paid plans
- Accepts any string written using github [markdown](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax)
- The default title to display is: "[ImgBot] Optimize images"

`.imgbotconfig`

```
{
"prTitle": "My title"
}
```
Loading

0 comments on commit 1411d56

Please sign in to comment.