Change up the org backend a bit to support members
All checks were successful
Deploy API / deploy (ubuntu-latest, 2.44.0) (push) Successful in 52s

This commit is contained in:
Braydon 2024-09-20 03:21:27 -04:00
parent c5841402f3
commit 58b7b69bb3
8 changed files with 109 additions and 24 deletions

@ -21,7 +21,7 @@ public final class DetailedOrganization extends cc.pulseapp.api.model.org.Organi
@NonNull private final List<StatusPage> statusPages;
public DetailedOrganization(@NonNull cc.pulseapp.api.model.org.Organization origin, @NonNull List<StatusPage> statusPages) {
super(origin.getSnowflake(), origin.getName(), origin.getSlug(), origin.getLogo(), origin.getOwnerSnowflake());
super(origin.getSnowflake(), origin.getName(), origin.getSlug(), origin.getLogo(), origin.getMembers(), origin.getOwnerSnowflake());
this.statusPages = statusPages;
}
}

@ -6,6 +6,7 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
import java.util.regex.Pattern;
/**
@ -39,6 +40,11 @@ public class Organization {
*/
private final String logo;
/**
* The members of this organization.
*/
@NonNull private final List<OrganizationMember> members;
/**
* The snowflake of the {@link User}
* that owns this organization.

@ -0,0 +1,25 @@
package cc.pulseapp.api.model.org;
import cc.pulseapp.api.model.user.User;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
/**
* A member of an {@link Organization}.
*
* @author Braydon
*/
@AllArgsConstructor @Getter @ToString
public final class OrganizationMember {
/**
* The snowflake of the {@link User}
* this member belongs to.
*/
private final long userSnowflake;
/**
* The bitwise permissions of this member.
*/
private final int permissions;
}

@ -0,0 +1,53 @@
package cc.pulseapp.api.model.org;
import cc.pulseapp.api.model.page.StatusPage;
/**
* The permissions of a {@link OrganizationMember}.
*
* @author Braydon
*/
public enum OrganizationMemberPermission {
/**
* The member can create, edit, and delete
* {@link StatusPage}s for the organization.
*/
MANAGE_STATUS_PAGES,
/**
* The member can create, edit, and
* delete automations for the organization.
*/
MANAGE_AUTOMATIONS,
/**
* The member can create, edit, and
* delete incidents for the organization.
*/
MANAGE_INCIDENTS,
/**
* The member can view insights of the organization.
*/
VIEW_INSIGHTS,
/**
* The member can view audit logs of the organization.
*/
VIEW_AUDIT_LOGS,
/**
* The member can edit the organization.
*/
MANAGE_ORGANIZATION;
/**
* Get the bitwise value
* of this permission.
*
* @return the bitwise value
*/
public int bitwise() {
return 1 << ordinal();
}
}

@ -60,7 +60,7 @@ public final class User {
private TFAProfile tfa;
/**
* The flags for this user.
* The bitwise flags for this user.
*/
private int flags;

@ -3,6 +3,7 @@ package cc.pulseapp.api.repository;
import cc.pulseapp.api.model.org.Organization;
import lombok.NonNull;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
@ -15,12 +16,12 @@ import java.util.List;
@Repository
public interface OrganizationRepository extends MongoRepository<Organization, Long> {
/**
* Find an organization by its name.
* Get the organization that has the given slug.
*
* @param name the name of the org
* @return the org with the name
* @param slug the organization slug
* @return the organization, null if none
*/
Organization findByNameIgnoreCase(@NonNull String name);
Organization findBySlug(@NonNull String slug);
/**
* Get the organizations that
@ -30,4 +31,14 @@ public interface OrganizationRepository extends MongoRepository<Organization, Lo
* @return the owned organizations
*/
List<Organization> findByOwnerSnowflake(long ownerSnowflake);
/**
* Get the organizations that the user
* either owns or is a member of.
*
* @param userSnowflake the user's snowflake
* @return the organizations the user has access to
*/
@Query("{ '$or': [ { 'ownerSnowflake': ?0 }, { 'members.userSnowflake': ?0 } ] }")
List<Organization> findByUserAccess(long userSnowflake);
}

@ -19,12 +19,4 @@ public interface UserRepository extends MongoRepository<User, Long> {
* @return the user with the email
*/
User findByEmailIgnoreCase(@NonNull String email);
/**
* Find a user by their username.
*
* @param username the username of the user
* @return the user with the username
*/
User findByUsernameIgnoreCase(@NonNull String username);
}

@ -15,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
@ -66,21 +67,18 @@ public final class OrganizationService {
if (!Feature.ORG_CREATION_ENABLED.isEnabled()) {
throw new BadRequestException(Error.ORG_CREATION_DISABLED);
}
List<Organization> ownedOrgs = orgRepository.findByOwnerSnowflake(owner.getSnowflake());
// Ensure the org name isn't taken
if (ownedOrgs.stream().anyMatch(org -> org.getName().equalsIgnoreCase(name))) {
throw new BadRequestException(Error.ORG_NAME_TAKEN);
// Ensure the org slug isn't taken
if (orgRepository.findBySlug(slug) != null) {
throw new BadRequestException(Error.ORG_SLUG_TAKEN);
}
// Handle cloud environment checks
if (EnvironmentUtils.isCloud()) {
if (ownedOrgs.size() >= owner.getTier().getMaxOrganizations()) {
if (orgRepository.findByOwnerSnowflake(owner.getSnowflake()).size() >= owner.getTier().getMaxOrganizations()) {
throw new BadRequestException(Error.MAX_ORGS_REACHED);
}
}
// Create the org and return it
slug = slug.trim().replaceAll("-+$", ""); // Trim slug trailing dashes
return orgRepository.save(new Organization(snowflakeService.generateSnowflake(), name, slug, null, owner.getSnowflake()));
return orgRepository.save(new Organization(snowflakeService.generateSnowflake(), name, slug, null, Collections.emptyList(), owner.getSnowflake()));
}
/**
@ -93,7 +91,7 @@ public final class OrganizationService {
public List<DetailedOrganization> getOrganizations() {
User user = authService.getAuthenticatedUser();
List<DetailedOrganization> organizations = new ArrayList<>();
for (Organization org : orgRepository.findByOwnerSnowflake(user.getSnowflake())) {
for (Organization org : orgRepository.findByUserAccess(user.getSnowflake())) {
organizations.add(new DetailedOrganization(org, statusPageRepository.findByOrgSnowflake(org.getSnowflake())));
}
return organizations;
@ -104,7 +102,7 @@ public final class OrganizationService {
*/
private enum Error implements IGenericResponse {
ORG_CREATION_DISABLED,
ORG_NAME_TAKEN,
ORG_SLUG_TAKEN,
MAX_ORGS_REACHED
}
}