Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions src/models/userEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export class UserEntry extends Entry {
email: string;
disabled = false;
deleted = false;
creationDate: Date = null;
deletionDate: Date = null;

get displayName(): string {
if (this.email == null) {
Expand All @@ -12,4 +14,18 @@ export class UserEntry extends Entry {

return this.email;
}

get relevantDate(): Date {
if (this.deleted) {
return this.deletionDate;
}
return this.creationDate;
}

newerThan(other: UserEntry): boolean {
if (this.relevantDate != null && other.relevantDate != null) {
return this.relevantDate > other.relevantDate;
}
return false;
}
}
2 changes: 2 additions & 0 deletions src/services/gsuite-directory.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ export class GSuiteDirectoryService extends BaseDirectoryService implements IDir
entry.email = user.primaryEmail != null ? user.primaryEmail.trim().toLowerCase() : null;
entry.disabled = user.suspended || false;
entry.deleted = deleted;
entry.creationDate = user.creationTime != null ? new Date(user.creationTime) : null;
entry.deletionDate = user.deletionTime != null ? new Date(user.deletionTime) : null;
return entry;
}

Expand Down
42 changes: 23 additions & 19 deletions src/services/sync.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,33 +138,37 @@ export class SyncService {
return null;
}

const userMap = new Map<string, UserEntry[]>();
const uniqueUsers = new Array<UserEntry>();
const processedActiveUsers = new Map<string, string>();
const processedDeletedUsers = new Map<string, string>();
const duplicateEmails = new Array<string>();

// UserEntrys with the same email are ignored if their properties are the same
// UserEntrys with the same email but different properties will throw an error, unless they are all in a deleted state.
// Map users by email address
users.forEach((u) => {
if (processedActiveUsers.has(u.email)) {
if (processedActiveUsers.get(u.email) !== JSON.stringify(u)) {
duplicateEmails.push(u.email);
}
} else {
if (!u.deleted) {
// Check that active UserEntry does not conflict with a deleted UserEntry
if (processedDeletedUsers.has(u.email)) {
userMap.set(u.email, userMap.get(u.email) || []);
userMap.get(u.email).push(u);
});

// We only care about the most recent entry. If there are multiple entries for the same email, all except the most recent must be either
// deleted, or have identical properties.
userMap.forEach((us) => {
// If there are multiple entries, we want to process the newest one first.
us = us.sort((a, b) => { return a.newerThan(b) ? -1 : 1; });
const [head, ...tail] = us;
uniqueUsers.push(head);
const comparison = JSON.stringify(head);
tail.forEach((u) => {
if (head.deleted) {
// If the latest entry is deleted, all other entries also must be deleted
if (!u.deleted) {
duplicateEmails.push(u.email);
} else {
processedActiveUsers.set(u.email, JSON.stringify(u));
uniqueUsers.push(u);
}
} else {
// UserEntrys with duplicate email will not throw an error if they are all deleted. They will be synced.
processedDeletedUsers.set(u.email, JSON.stringify(u));
uniqueUsers.push(u);
// If the latest entry is active, all other entries must be deleted, or identical.
if (!u.deleted && comparison !== JSON.stringify(u)) {
duplicateEmails.push(u.email);
}
}
}
});
});

if (duplicateEmails.length > 0) {
Expand Down