Skip to content

Commit 1b8fed6

Browse files
✨ Fix users recuperation to not remove all users before gett… (#34)
* ✨ Fix users recuperation to not remove all users before getting in GitHub * 🎨 Add links on GitHub and GitLab profile
1 parent c759c49 commit 1b8fed6

4 files changed

Lines changed: 113 additions & 13 deletions

File tree

src/main/java/zenika/oss/stats/ressources/workflow/WorkflowRessources.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import zenika.oss.stats.services.GitHubServices;
2121

2222
import java.util.List;
23+
import java.util.Set;
24+
import java.util.stream.Collectors;
2325

2426
@ApplicationScoped
2527
@Path("/v1/workflow/")
@@ -36,12 +38,39 @@ public class WorkflowRessources {
3638
@Produces(MediaType.TEXT_PLAIN)
3739
public Response saveMembers() throws DatabaseException {
3840

39-
firestoreServices.deleteAllMembers();
40-
41+
// Load current state
42+
List<ZenikaMember> existingMembers = firestoreServices.getAllMembers();
4143
List<GitHubMember> gitHubMembers = gitHubServices.getZenikaOpenSourceMembers();
42-
// Use a for-loop to properly handle the checked DatabaseException
44+
45+
// Build a set of current GitHub logins in the organization
46+
Set<String> currentGitHubLogins = gitHubMembers.stream()
47+
.map(GitHubMember::getLogin)
48+
.collect(Collectors.toSet());
49+
50+
// Upsert: add new GitHub members and update existing ones
4351
for (GitHubMember gitHubMember : gitHubMembers) {
44-
firestoreServices.createMember(ZenikaMemberMapper.mapGitHubMemberToZenikaMember(gitHubMember));
52+
ZenikaMember existing = existingMembers.stream()
53+
.filter(m -> m.getGitHubAccount() != null
54+
&& gitHubMember.getLogin().equals(m.getGitHubAccount().getLogin()))
55+
.findFirst()
56+
.orElse(null);
57+
58+
if (existing == null) {
59+
// New member from GitHub organization
60+
firestoreServices.createMember(ZenikaMemberMapper.mapGitHubMemberToZenikaMember(gitHubMember));
61+
} else {
62+
// Keep the same ZenikaMember (id, city, GitLab, etc.) but refresh GitHub data
63+
existing.setGitHubAccount(gitHubMember);
64+
firestoreServices.createMember(existing);
65+
}
66+
}
67+
68+
// Cleanup: remove members whose GitHub account is no longer in the organization
69+
for (ZenikaMember member : existingMembers) {
70+
if (member.getGitHubAccount() != null
71+
&& !currentGitHubLogins.contains(member.getGitHubAccount().getLogin())) {
72+
firestoreServices.deleteMember(member.getId());
73+
}
4574
}
4675

4776
return Response.ok().build();

src/main/java/zenika/oss/stats/services/FirestoreServices.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,24 @@ public void deleteAllMembers() throws DatabaseException {
163163
deleteAllDocuments(FirestoreCollections.MEMBERS);
164164
}
165165

166+
/**
167+
* Delete a single member by id.
168+
*
169+
* @param memberId the id of the member document to delete.
170+
* @throws DatabaseException exception
171+
*/
172+
@CacheInvalidateAll(cacheName = "members-cache")
173+
public void deleteMember(String memberId) throws DatabaseException {
174+
try {
175+
firestore.collection(FirestoreCollections.MEMBERS.value)
176+
.document(memberId)
177+
.delete()
178+
.get();
179+
} catch (InterruptedException | ExecutionException e) {
180+
throw new DatabaseException(e);
181+
}
182+
}
183+
166184
/**
167185
* Create a document in the Firestore database.
168186
*

src/main/java/zenika/oss/stats/ui/tabs/MembersTab.java

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
import java.util.Comparator;
1717
import java.util.List;
18+
import java.util.Set;
19+
import java.util.stream.Collectors;
1820

1921
@ApplicationScoped
2022
public class MembersTab {
@@ -65,13 +67,48 @@ public void render(JtContainer membersTab) {
6567

6668
if (Jt.button("🔄 Sync Members from GitHub").use(columns.col(1))) {
6769
try {
68-
firestoreServices.deleteAllMembers();
70+
// Load current state from Firestore and GitHub
71+
List<ZenikaMember> existingMembers = firestoreServices.getAllMembers();
6972
List<GitHubMember> gitHubMembers = gitHubServices.getZenikaOpenSourceMembers();
70-
73+
74+
Set<String> currentGitHubLogins = gitHubMembers.stream()
75+
.map(GitHubMember::getLogin)
76+
.collect(Collectors.toSet());
77+
78+
int added = 0;
79+
int updated = 0;
80+
int removed = 0;
81+
82+
// Upsert: add/update members based on GitHub organization
7183
for (GitHubMember gitHubMember : gitHubMembers) {
72-
firestoreServices.createMember(ZenikaMemberMapper.mapGitHubMemberToZenikaMember(gitHubMember));
84+
ZenikaMember existing = existingMembers.stream()
85+
.filter(m -> m.getGitHubAccount() != null
86+
&& gitHubMember.getLogin().equals(m.getGitHubAccount().getLogin()))
87+
.findFirst()
88+
.orElse(null);
89+
90+
if (existing == null) {
91+
firestoreServices.createMember(
92+
ZenikaMemberMapper.mapGitHubMemberToZenikaMember(gitHubMember));
93+
added++;
94+
} else {
95+
existing.setGitHubAccount(gitHubMember);
96+
firestoreServices.createMember(existing);
97+
updated++;
98+
}
99+
}
100+
101+
// Remove members that are no longer in the GitHub organization
102+
for (ZenikaMember member : existingMembers) {
103+
if (member.getGitHubAccount() != null
104+
&& !currentGitHubLogins.contains(member.getGitHubAccount().getLogin())) {
105+
firestoreServices.deleteMember(member.getId());
106+
removed++;
107+
}
73108
}
74-
Jt.success("Successfully synced " + gitHubMembers.size() + " members!").use(membersTab);
109+
110+
Jt.success("✅ Synchronization completed: " + added + " added, " + updated + " updated, " + removed
111+
+ " removed.").use(membersTab);
75112
} catch (DatabaseException e) {
76113
Jt.error("Error syncing members: " + e.getMessage()).use(membersTab);
77114
}
@@ -102,8 +139,26 @@ public void render(JtContainer membersTab) {
102139
var row = Jt.columns(6).key("member_row_" + m.getId()).use(membersTab);
103140
Jt.text(m.getFirstname() != null ? m.getFirstname() : "").use(row.col(0));
104141
Jt.text(m.getName() != null ? m.getName() : "").use(row.col(1));
105-
Jt.text(m.getGitHubAccount() != null ? m.getGitHubAccount().getLogin() : "").use(row.col(2));
106-
Jt.text(m.getGitlabAccount() != null ? m.getGitlabAccount().getUsername() : "").use(row.col(3));
142+
143+
if (m.getGitHubAccount() != null && m.getGitHubAccount().getLogin() != null) {
144+
String githubLogin = m.getGitHubAccount().getLogin();
145+
String githubLinkMarkdown = "<a href=\"https://github.com/" + githubLogin
146+
+ "\" target=\"_blank\" rel=\"noopener noreferrer\">"
147+
+ githubLogin + "</a>";
148+
Jt.markdown(githubLinkMarkdown).use(row.col(2));
149+
} else {
150+
Jt.text("").use(row.col(2));
151+
}
152+
153+
if (m.getGitlabAccount() != null && m.getGitlabAccount().getUsername() != null) {
154+
String gitlabUsername = m.getGitlabAccount().getUsername();
155+
String gitlabLinkMarkdown = "<a href=\"https://gitlab.com/" + gitlabUsername
156+
+ "\" target=\"_blank\" rel=\"noopener noreferrer\">"
157+
+ gitlabUsername + "</a>";
158+
Jt.markdown(gitlabLinkMarkdown).use(row.col(3));
159+
} else {
160+
Jt.text("").use(row.col(3));
161+
}
107162
Jt.text(m.getCity() != null ? m.getCity() : "").use(row.col(4));
108163
if (Jt.button("📝").key("btn_edit_" + m.getId()).use(row.col(5))) {
109164
selectedMemberId = m.getId();
@@ -128,7 +183,7 @@ public void render(JtContainer membersTab) {
128183
m.setCity(newCity);
129184
firestoreServices.createMember(m);
130185
selectedMemberId = null;
131-
Jt.success("Successfully updated").use(membersTab);
186+
Jt.success("Successfully updated").use(membersTab);
132187
Jt.markdown("<style>#edit_row_" + m.getId() + " { display: none !important; }</style>").use(membersTab);
133188
}
134189
if (Jt.button("Cancel").key("btn_cancel_" + m.getId()).use(editRow.col(5))) {

src/main/java/zenika/oss/stats/ui/tabs/ProjectsTab.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,6 @@ record ProjectDisplay(String id, String name, String fullName, String url, Long
109109
Jt.text("no data available").use(projectsTab);
110110
}
111111

112-
Jt.text("Fetch and save personal projects (not forked) for all saved members.").use(projectsTab);
113-
114112
} catch (Exception e) {
115113
Jt.warning("Could not load current projects: " + e.getMessage()).use(projectsTab);
116114
LOG.error("Could not load current projects", e);

0 commit comments

Comments
 (0)