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
All checks were successful
Deploy API / deploy (ubuntu-latest, 2.44.0) (push) Successful in 52s
This commit is contained in:
parent
c5841402f3
commit
58b7b69bb3
@ -21,7 +21,7 @@ public final class DetailedOrganization extends cc.pulseapp.api.model.org.Organi
|
|||||||
@NonNull private final List<StatusPage> statusPages;
|
@NonNull private final List<StatusPage> statusPages;
|
||||||
|
|
||||||
public DetailedOrganization(@NonNull cc.pulseapp.api.model.org.Organization origin, @NonNull 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;
|
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.index.Indexed;
|
||||||
import org.springframework.data.mongodb.core.mapping.Document;
|
import org.springframework.data.mongodb.core.mapping.Document;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,6 +40,11 @@ public class Organization {
|
|||||||
*/
|
*/
|
||||||
private final String logo;
|
private final String logo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The members of this organization.
|
||||||
|
*/
|
||||||
|
@NonNull private final List<OrganizationMember> members;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The snowflake of the {@link User}
|
* The snowflake of the {@link User}
|
||||||
* that owns this organization.
|
* 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;
|
private TFAProfile tfa;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The flags for this user.
|
* The bitwise flags for this user.
|
||||||
*/
|
*/
|
||||||
private int flags;
|
private int flags;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package cc.pulseapp.api.repository;
|
|||||||
import cc.pulseapp.api.model.org.Organization;
|
import cc.pulseapp.api.model.org.Organization;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||||
|
import org.springframework.data.mongodb.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -15,12 +16,12 @@ import java.util.List;
|
|||||||
@Repository
|
@Repository
|
||||||
public interface OrganizationRepository extends MongoRepository<Organization, Long> {
|
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
|
* @param slug the organization slug
|
||||||
* @return the org with the name
|
* @return the organization, null if none
|
||||||
*/
|
*/
|
||||||
Organization findByNameIgnoreCase(@NonNull String name);
|
Organization findBySlug(@NonNull String slug);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the organizations that
|
* Get the organizations that
|
||||||
@ -30,4 +31,14 @@ public interface OrganizationRepository extends MongoRepository<Organization, Lo
|
|||||||
* @return the owned organizations
|
* @return the owned organizations
|
||||||
*/
|
*/
|
||||||
List<Organization> findByOwnerSnowflake(long ownerSnowflake);
|
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
|
* @return the user with the email
|
||||||
*/
|
*/
|
||||||
User findByEmailIgnoreCase(@NonNull String 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 org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,21 +67,18 @@ public final class OrganizationService {
|
|||||||
if (!Feature.ORG_CREATION_ENABLED.isEnabled()) {
|
if (!Feature.ORG_CREATION_ENABLED.isEnabled()) {
|
||||||
throw new BadRequestException(Error.ORG_CREATION_DISABLED);
|
throw new BadRequestException(Error.ORG_CREATION_DISABLED);
|
||||||
}
|
}
|
||||||
List<Organization> ownedOrgs = orgRepository.findByOwnerSnowflake(owner.getSnowflake());
|
// Ensure the org slug isn't taken
|
||||||
|
if (orgRepository.findBySlug(slug) != null) {
|
||||||
// Ensure the org name isn't taken
|
throw new BadRequestException(Error.ORG_SLUG_TAKEN);
|
||||||
if (ownedOrgs.stream().anyMatch(org -> org.getName().equalsIgnoreCase(name))) {
|
|
||||||
throw new BadRequestException(Error.ORG_NAME_TAKEN);
|
|
||||||
}
|
}
|
||||||
// Handle cloud environment checks
|
// Handle cloud environment checks
|
||||||
if (EnvironmentUtils.isCloud()) {
|
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);
|
throw new BadRequestException(Error.MAX_ORGS_REACHED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create the org and return it
|
// Create the org and return it
|
||||||
slug = slug.trim().replaceAll("-+$", ""); // Trim slug trailing dashes
|
return orgRepository.save(new Organization(snowflakeService.generateSnowflake(), name, slug, null, Collections.emptyList(), owner.getSnowflake()));
|
||||||
return orgRepository.save(new Organization(snowflakeService.generateSnowflake(), name, slug, null, owner.getSnowflake()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,7 +91,7 @@ public final class OrganizationService {
|
|||||||
public List<DetailedOrganization> getOrganizations() {
|
public List<DetailedOrganization> getOrganizations() {
|
||||||
User user = authService.getAuthenticatedUser();
|
User user = authService.getAuthenticatedUser();
|
||||||
List<DetailedOrganization> organizations = new ArrayList<>();
|
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())));
|
organizations.add(new DetailedOrganization(org, statusPageRepository.findByOrgSnowflake(org.getSnowflake())));
|
||||||
}
|
}
|
||||||
return organizations;
|
return organizations;
|
||||||
@ -104,7 +102,7 @@ public final class OrganizationService {
|
|||||||
*/
|
*/
|
||||||
private enum Error implements IGenericResponse {
|
private enum Error implements IGenericResponse {
|
||||||
ORG_CREATION_DISABLED,
|
ORG_CREATION_DISABLED,
|
||||||
ORG_NAME_TAKEN,
|
ORG_SLUG_TAKEN,
|
||||||
MAX_ORGS_REACHED
|
MAX_ORGS_REACHED
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user