Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes and Click once installer #3

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
585c445
fix moving card to column. The problem was in the method to get a car…
yosoymin Aug 5, 2020
ec0a05b
update to last plastic version
yosoymin Aug 5, 2020
3c9d303
WIP: installer
yosoymin Aug 5, 2020
a0cc7a3
WIP: uninstall
yosoymin Aug 5, 2020
aff38ab
Instalador listo
yosoymin Aug 5, 2020
f0152f8
Fixed issue #1
yosoymin Aug 6, 2020
254186e
fix AddCommentToCard response parsing
yosoymin Aug 6, 2020
512c483
new plastic dlls
yosoymin Aug 12, 2020
20bb022
Not copying to dlls to ProgramFiles, no longer needed to launch VS as…
yosoymin Aug 13, 2020
178f608
fix publicKeyTokens
yosoymin Aug 13, 2020
a58afb4
migrate packages.config to PackageReference
yosoymin Aug 13, 2020
fa09473
cosmetic tab
yosoymin Aug 14, 2020
4aaf282
instructions to install in linux
yosoymin Sep 6, 2020
5bece6f
improved support for cards that lives in more than one board. Some bo…
yosoymin Sep 25, 2020
c8720ad
update to new plastic version
yosoymin Oct 26, 2020
b787ca3
support for member groups in cards
yosoymin Dec 15, 2020
d86a3bd
update to plastic version 9.0.16.5140
yosoymin Mar 6, 2021
b50738f
new version of plastic 9.0.16.5285
yosoymin Mar 30, 2021
beff607
.
yosoymin Mar 30, 2021
0212858
update to new Plastic version
yosoymin Aug 30, 2021
5a3a9fe
showing user histories in the pending tasks
yosoymin Oct 6, 2021
6b226a2
update to Plastic version 10.0.16.6064
yosoymin Oct 7, 2021
c1a17b5
Update to Plastic SCM 10.0.16.6241
jamilan Nov 23, 2021
6584e17
Set next version
jamilan Nov 23, 2021
748b018
Update README.md
jamilan Nov 23, 2021
72baa24
Update to plastic scm version 11.0.16.6757
yosoymin Mar 26, 2022
52750be
rebuild
yosoymin Mar 26, 2022
53e23b5
Merge branch 'master' of https://github.com/yosoymin/FavroPlasticExte…
yosoymin Mar 26, 2022
4745faa
new plastic version support
yosoymin Aug 16, 2022
e37a98a
update to latests plastic version
bfuente Sep 28, 2022
e2160d1
update to Plastic version 11.0.16.7696
yosoymin Jan 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions FavroPlasticExtension.sln
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
# Visual Studio Version 16
VisualStudioVersion = 16.0.30320.27
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FavroPlasticExtension", "FavroPlasticExtension\FavroPlasticExtension.csproj", "{EE317B58-63A7-4480-9EC2-C860682B5253}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FavroPlasticExtensionTests", "FavroPlasticExtensionTests\FavroPlasticExtensionTests.csproj", "{B88DA190-B686-4501-84C6-90FBD2158BE7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FavroPlasticExtensionInstaller", "FavroPlasticExtensionInstaller\FavroPlasticExtensionInstaller.csproj", "{D9A5FF15-2CF3-470D-AC91-8AE191E9C056}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -19,5 +22,15 @@ Global
{B88DA190-B686-4501-84C6-90FBD2158BE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B88DA190-B686-4501-84C6-90FBD2158BE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B88DA190-B686-4501-84C6-90FBD2158BE7}.Release|Any CPU.Build.0 = Release|Any CPU
{D9A5FF15-2CF3-470D-AC91-8AE191E9C056}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9A5FF15-2CF3-470D-AC91-8AE191E9C056}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9A5FF15-2CF3-470D-AC91-8AE191E9C056}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9A5FF15-2CF3-470D-AC91-8AE191E9C056}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B85DE185-E949-4D5A-B9F9-A3A8FDDD44DA}
EndGlobalSection
EndGlobal
19 changes: 19 additions & 0 deletions FavroPlasticExtension/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="issuetrackerinterface" publicKeyToken="a107c9c6e34c8876" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.16.4456" newVersion="9.0.16.4456"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-1.2.15.0" newVersion="1.2.15.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="utils" publicKeyToken="a107c9c6e34c8876" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.16.4456" newVersion="9.0.16.4456"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8.1"/></startup></configuration>
2 changes: 1 addition & 1 deletion FavroPlasticExtension/Favro/API/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ private Response ParseWebResponse(HttpWebRequest request)
if (int.TryParse(webResponse.Headers[Response.HEADER_LIMIT_MAX], out int limit))
{
RequestLimit = limit;
}
}
response.Headers.Add(Response.HEADER_BACKEND_ID, webResponse.Headers[Response.HEADER_BACKEND_ID]);
response.Headers.Add(Response.HEADER_LIMIT_MAX, webResponse.Headers[Response.HEADER_LIMIT_MAX]);
response.Headers.Add(Response.HEADER_LIMIT_REMAINING, webResponse.Headers[Response.HEADER_LIMIT_REMAINING]);
Expand Down
101 changes: 69 additions & 32 deletions FavroPlasticExtension/Favro/API/FavroApiFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public List<Card> GetAssignedCards(string collectionId, string widgetCommonId)
}
}

public Card GetCard(string commonId)
public List<Card> GetCards(string commonId)
{
if (commonId == null)
{
Expand All @@ -166,28 +166,10 @@ public Card GetCard(string commonId)
{
{ "cardCommonId", commonId }
};
return GetCard(parameters);
return GetCards(parameters);
}

private Card GetFirstCardWithColumn(List<Card> cards)
{
if (cards != null && cards.Count > 0)
{
var cardWithColumn = cards.Find(card => card.ColumnId != null);
if (cardWithColumn != null)
{
return cardWithColumn;
}
else
{
return cards[0];
}
}
else
return null;
}

public Card GetCard(int sequentialId)
public List<Card> GetCards(int sequentialId)
{
if (sequentialId < 0)
{
Expand All @@ -197,14 +179,42 @@ public Card GetCard(int sequentialId)
{
{ "cardSequentialId", sequentialId.ToString() }
};
return GetCard(parameters);
return GetCards(parameters);
}

public Card GetCardById(string cardId)
{
if (cardId == null)
{
throw new ArgumentNullException(nameof(cardId), "A card identifier must be a non-empty string");
}
if (string.IsNullOrWhiteSpace(cardId))
{
throw new ArgumentException("A card common identifier must be a non-empty string", nameof(cardId));
}
CheckOrganizationSelected();
return GetFromEndpoint<Card>($"{ENDPOINT_CARDS}/{cardId}", NO_PARAMS, "Unexpected error while retrieving card by id");
}

private Card GetCard(NameValueCollection parameters)
private List<Card> GetCards(NameValueCollection parameters)
{
CheckOrganizationSelected();
parameters.Add("unique", "true");
return GetFirstCardWithColumn(GetAllPagesFromEndpoint<Card>(ENDPOINT_CARDS, parameters, "Unexpected error while retrieving card by id"));
return GetAllPagesFromEndpoint<Card>(ENDPOINT_CARDS, parameters, "Unexpected error while retrieving card by id");
}

public string GetParentCardId(Card card)
{
if (card.ParentCardId != null)
return card.ParentCardId;

var cardsCommon = GetCards(card.CardCommonId);
foreach (var commonCard in cardsCommon)
{
if (commonCard.ParentCardId != null)
return commonCard.ParentCardId;
}

return null;
}

public Card CompleteCard(string cardCommonId)
Expand Down Expand Up @@ -237,21 +247,36 @@ public CardComment AddCommentToCard(string cardCommonId, string comment)
parameters.Add("cardCommonId", cardCommonId);
parameters.Add("comment", comment);
var response = connection.Post($"{ENDPOINT_COMMENTS}", parameters);
return GetEntries<CardComment>(response).FirstOrDefault();
return JsonConvert.DeserializeObject<CardComment>(response.Content);
}

public void MoveCardToColumn(Card card, Column column)
{
var parameters = new Dictionary<string, string>();
parameters.Add("widgetCommonId", card.WidgetCommonId);
parameters.Add("columnId", column.ColumnId);
connection.Put($"{ENDPOINT_CARDS}/{card.CardId}", parameters);
if (column != null)
{
var parameters = new Dictionary<string, string>();
parameters.Add("widgetCommonId", card.WidgetCommonId);
parameters.Add("columnId", column.ColumnId);
parameters.Add("dragMode", "move");
connection.Put($"{ENDPOINT_CARDS}/{card.CardId}", parameters);
}
else
throw new ArgumentNullException(nameof(column), "A column can't be null when moving cards to column");
}

private List<TEntry> GetEntries<TEntry>(Response response)
{
var deserializedContent = JObject.Parse(response.Content);
return deserializedContent["entities"].Select(entry => JsonConvert.DeserializeObject<TEntry>(entry.ToString())).ToList();
try
{
var deserializedContent = JObject.Parse(response.Content);
return deserializedContent["entities"].Select(entry => JsonConvert.DeserializeObject<TEntry>(entry.ToString())).ToList();
}
catch (Exception e)
{
log.Error(e.Message);
// It's important for caches like column cache to return something to avoid querying Favro all the time in case of permission error or similar reasons
return new List<TEntry>();
}
}

private void CheckUserParameter(string userId)
Expand Down Expand Up @@ -282,5 +307,17 @@ private List<TEntry> GetAllPagesFromEndpoint<TEntry>(string endpoint, NameValueC
}
return entries;
}

private TEntry GetFromEndpoint<TEntry>(string endpoint, NameValueCollection paramenters, string errorMessage)
{
var response = connection.Get(endpoint, paramenters);
if (response.Error != null)
{
log.Error(errorMessage, response.Error);
return default;
}
return JsonConvert.DeserializeObject<TEntry>(response.Content);
}

}
}
7 changes: 0 additions & 7 deletions FavroPlasticExtension/Favro/API/Response.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,5 @@ public string GetRequestId()
var data = GetDeserializedData();
return data.GetValue(PATH_REQUEST_ID).Value<string>();
}

public string GetEntitiesString()
{
var data = GetDeserializedData();
var entities = data.GetValue(PATH_ENTITIES);
return JsonConvert.SerializeObject(entities);
}
}
}
87 changes: 74 additions & 13 deletions FavroPlasticExtension/FavroExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,30 @@ private Column FindColumn(string widgetCommonId, string name)

public void Connect()
{
connection = CreateConnection(configuration);
var organization = configuration.GetValue(KEY_ORGANIZATION);
connection.OrganizationId = organization;
apiMethods = new FavroApiFacade(connection, logger);
organizationInfo = apiMethods.GetOrganization(organization);
organizationShortName = GetOrganizationShortName();
try
{
connection = CreateConnection(configuration);
apiMethods = new FavroApiFacade(connection, logger);
var organization = configuration.GetValue(KEY_ORGANIZATION);
organizationInfo = apiMethods.GetOrganization(organization);
if (organizationInfo != null)
{
connection.OrganizationId = organizationInfo.OrganizationId;
organizationShortName = GetOrganizationShortName();
}
else
{
throw new InvalidOperationException("The organization info couldn't be retrieved from Favro, check your user and password");
}
}
catch (Exception e)
{
// If the plugin is not well configured the previous code throws exceptions, but
// we can't throw an expection in this method because in other case Plastic can't work
// properly and both the preferences dialog and the branch explorer can't be opened until
// this plugin is uninstalled
logger.Error(e.Message);
}
}

public void Disconnect()
Expand Down Expand Up @@ -193,16 +211,42 @@ public List<PlasticTask> LoadTasks(List<string> taskIds)
return result;
}

public List<PlasticTask> GetPendingTasks()
private List<PlasticTask> GetPendingTasksInternal(string assignee)
{
var pendingCards = apiMethods.GetAssignedCards(GetCollectionId(), GetWidgetCommonId());
return pendingCards.Where(card => card.Assignments.Find(assignee => !assignee.Completed) != null).Select(_ => ConvertToTask(_)).Where(task => task.CanBeLinked).ToList();
var pendingNotCompleted = pendingCards.Where(card => card.Assignments.Find(_assignee => !_assignee.Completed) != null);
var pendingTasks = new List<PlasticTask>();
var parentCardIds = new List<string>();
foreach (var card in pendingNotCompleted)
{
var task = ConvertToTask(card);
if (task.CanBeLinked && (assignee == null || assignee == task.Owner))
{
var parentId = apiMethods.GetParentCardId(card);
// only add if not previously added
if (parentId != null && parentCardIds.Find(id => id == parentId) == null)
{
parentCardIds.Add(parentId);
var parentCard = apiMethods.GetCardById(parentId);
if (parentCard != null)
{
pendingTasks.Add(ConvertToTask(parentCard));
}
}
pendingTasks.Add(task);
}
}
return pendingTasks;
}

public List<PlasticTask> GetPendingTasks()
{
return GetPendingTasksInternal(null);
}

public List<PlasticTask> GetPendingTasks(string assignee)
{
var tasks = GetPendingTasks();
return tasks.Where(task => task.Owner == assignee).ToList();
return GetPendingTasksInternal(assignee);
}

public void MarkTaskAsOpen(string taskId, string assignee)
Expand Down Expand Up @@ -307,10 +351,27 @@ private string GetWidgetCommonId()
return configuration.GetValue(KEY_WIDGET_ID);
}

private Card GetFirstCardWithColumn(List<Card> cards)
{
if (cards != null && cards.Count > 0)
{
var cardsWithColumn = cards.FindAll(card => card.ColumnId != null && FindColumn(card) != null);
var cardWithSameWidgetCommonId = cardsWithColumn.Find(card => card.WidgetCommonId == GetWidgetCommonId());
if (cardWithSameWidgetCommonId != null)
return cardWithSameWidgetCommonId;
else if (cardsWithColumn.Count > 0)
return cardsWithColumn[0];
else
return cards[0];
}
else
return null;
}

private Card GetCardFromSequentialId(string cardId)
{
if (!string.IsNullOrEmpty(cardId) && int.TryParse(cardId, out int cardSequentialId))
return apiMethods.GetCard(cardSequentialId);
return GetFirstCardWithColumn(apiMethods.GetCards(cardSequentialId));
else
return null;
}
Expand All @@ -332,7 +393,7 @@ private PlasticTask ConvertToTask(Card card)
CheckExistsUsersCache();
// If the plastic user is in the asignments list, assume he is the owner
var currentUserMail = configuration.GetValue(KEY_USER);
var currentUser = card.Assignments.Find(assignee => usersCache[assignee.UserId].Email == currentUserMail);
var currentUser = card.Assignments.Find(assignee => usersCache.ContainsKey(assignee.UserId) && usersCache[assignee.UserId].Email == currentUserMail);
CardAssignment firstPending = null;
if (currentUser == null)
{
Expand All @@ -341,7 +402,7 @@ private PlasticTask ConvertToTask(Card card)
firstPending = card.Assignments[0];
}
var owner = currentUser != null ? currentUser : firstPending;
string userMail = owner != null ? usersCache[owner.UserId].Email : "unknown";
string userMail = owner != null && usersCache.ContainsKey(owner.UserId) ? usersCache[owner.UserId].Email : "unknown";
var column = FindColumn(card);
string status = column != null ? column.Name : "unknown";
result = new PlasticTask
Expand Down
Loading