1
0

introduce-separate-database-schemas-hs-booking-and-hosting (#106)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/106
Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
Michael Hoennig
2024-09-23 10:52:37 +02:00
parent 23b60641e3
commit f33a3a2df7
101 changed files with 1072 additions and 1071 deletions
@@ -18,7 +18,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
// a partial HsOfficeDebitorEntity to reduce the number of SQL queries to load the entity
@Entity
@Table(name = "hs_booking_debitor_xv")
@Table(schema = "hs_booking", name = "debitor_xv")
@Getter
@Builder
@NoArgsConstructor
@@ -31,7 +31,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetc
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
@Entity
@Table(name = "hs_booking_item_rv")
@Table(schema = "hs_booking", name = "item_rv")
@SuperBuilder(toBuilder = true)
@Getter
@Setter
@@ -13,7 +13,7 @@ import jakarta.persistence.Table;
@Entity
@Table(name = "hs_booking_item")
@Table(schema = "hs_booking", name = "item")
@SuperBuilder(toBuilder = true)
@Getter
@Setter
@@ -71,7 +71,7 @@ public abstract class HsBookingProject implements Stringifyable, BaseEntity<HsBo
return rbacViewFor("project", HsBookingProjectRbacEntity.class)
.withIdentityView(SQL.query("""
SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || base.cleanIdentifier(bookingProject.caption) as idName
FROM hs_booking_project bookingProject
FROM hs_booking.project bookingProject
JOIN hs_office.debitor_iv debitorIV ON debitorIV.uuid = bookingProject.debitorUuid
"""))
.withRestrictedViewOrderBy(SQL.expression("caption"))
@@ -32,7 +32,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
@Entity
@Table(name = "hs_booking_project_rv")
@Table(schema = "hs_booking", name = "project_rv")
@SuperBuilder(toBuilder = true)
@Getter
@Setter
@@ -43,7 +43,7 @@ public class HsBookingProjectRbacEntity extends HsBookingProject {
return rbacViewFor("project", HsBookingProjectRbacEntity.class)
.withIdentityView(SQL.query("""
SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || base.cleanIdentifier(bookingProject.caption) as idName
FROM hs_booking_project bookingProject
FROM hs_booking.project bookingProject
JOIN hs_office.debitor_iv debitorIV ON debitorIV.uuid = bookingProject.debitorUuid
"""))
.withRestrictedViewOrderBy(SQL.expression("caption"))
@@ -10,7 +10,7 @@ import jakarta.persistence.Table;
@Entity
@Table(name = "hs_booking_project")
@Table(schema = "hs_booking", name = "project")
@SuperBuilder(toBuilder = true)
@Getter
@Setter
@@ -33,7 +33,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetc
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
@Entity
@Table(name = "hs_hosting_asset_rv")
@Table(schema = "hs_hosting", name = "asset_rv")
@SuperBuilder(toBuilder = true)
@Getter
@Setter
@@ -25,15 +25,15 @@ public interface HsHostingAssetRbacRepository extends HsHostingAssetRepository<H
ha.parentassetuuid,
ha.type,
ha.version
from hs_hosting_asset_rv ha
left join hs_booking_item bi on bi.uuid = ha.bookingitemuuid
left join hs_hosting_asset pha on pha.uuid = ha.parentassetuuid
from hs_hosting.asset_rv ha
left join hs_booking.item bi on bi.uuid = ha.bookingitemuuid
left join hs_hosting.asset pha on pha.uuid = ha.parentassetuuid
where (:projectUuid is null or bi.projectuuid=:projectUuid)
and (:parentAssetUuid is null or pha.uuid=:parentAssetUuid)
and (:type is null or :type=cast(ha.type as text))
""", nativeQuery = true)
// The JPQL query did not generate "left join" but just "join".
// I also optimized the query by not using the _rv for hs_booking_item and hs_hosting_asset, only for hs_hosting_asset_rv.
// I also optimized the query by not using the _rv for hs_booking.item and hs_hosting.asset, only for hs_hosting.asset_rv.
List<HsHostingAssetRbacEntity> findAllByCriteriaImpl(UUID projectUuid, UUID parentAssetUuid, String type);
default List<HsHostingAssetRbacEntity> findAllByCriteria(final UUID projectUuid, final UUID parentAssetUuid, final HsHostingAssetType type) {
return findAllByCriteriaImpl(projectUuid, parentAssetUuid, HsHostingAssetType.asString(type));
@@ -9,7 +9,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Table;
@Entity
@Table(name = "hs_hosting_asset")
@Table(schema = "hs_hosting", name = "asset")
@SuperBuilder(builderMethodName = "genericBuilder", toBuilder = true)
@Getter
@Setter
@@ -24,15 +24,15 @@ public interface HsHostingAssetRealRepository extends HsHostingAssetRepository<H
ha.parentassetuuid,
ha.type,
ha.version
from hs_hosting_asset_rv ha
left join hs_booking_item bi on bi.uuid = ha.bookingitemuuid
left join hs_hosting_asset pha on pha.uuid = ha.parentassetuuid
from hs_hosting.asset_rv ha
left join hs_booking.item bi on bi.uuid = ha.bookingitemuuid
left join hs_hosting.asset pha on pha.uuid = ha.parentassetuuid
where (:projectUuid is null or bi.projectuuid=:projectUuid)
and (:parentAssetUuid is null or pha.uuid=:parentAssetUuid)
and (:type is null or :type=cast(ha.type as text))
""", nativeQuery = true)
// The JPQL query did not generate "left join" but just "join".
// I also optimized the query by not using the _rv for hs_booking_item and hs_hosting_asset, only for hs_hosting_asset_rv.
// I also optimized the query by not using the _rv for hs_booking.item and hs_hosting.asset, only for hs_hosting.asset_rv.
List<HsHostingAssetRealEntity> findAllByCriteriaImpl(UUID projectUuid, UUID parentAssetUuid, String type);
default List<HsHostingAssetRealEntity> findAllByCriteria(final UUID projectUuid, final UUID parentAssetUuid, final HsHostingAssetType type) {
return findAllByCriteriaImpl(projectUuid, parentAssetUuid, HsHostingAssetType.asString(type));
@@ -53,7 +53,7 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator {
}
private static Integer computeUserId(final EntityManager em, final PropertiesProvider propertiesProvider) {
final Object result = em.createNativeQuery("SELECT nextval('hs_hosting_asset_unixuser_system_id_seq')", Integer.class)
final Object result = em.createNativeQuery("SELECT nextval('hs_hosting.asset_unixuser_system_id_seq')", Integer.class)
.getSingleResult();
return (Integer) result;
}
@@ -34,7 +34,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@Table(schema = "hs_office", name = "coopassetstransaction_rv")
@Table(schema = "hs_office", name = "coopassettx_rv")
@Getter
@Setter
@Builder
@@ -32,7 +32,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@Table(schema = "hs_office", name = "coopsharestransaction_rv")
@Table(schema = "hs_office", name = "coopsharetx_rv")
@Getter
@Setter
@Builder
@@ -24,7 +24,7 @@ public interface HsOfficeRelationRbacRepository extends Repository<HsOfficeRelat
@Query(value = """
SELECT p.* FROM hs_office.relation_rv AS p
WHERE (:relationType IS NULL OR p.type = cast(:relationType AS HsOfficeRelationType))
WHERE (:relationType IS NULL OR p.type = cast(:relationType AS hs_office.RelationType))
AND ( p.anchorUuid = :personUuid OR p.holderUuid = :personUuid)
""", nativeQuery = true)
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidAndRelationTypeString(@NotNull UUID personUuid, String relationType);
@@ -24,7 +24,7 @@ public interface HsOfficeRelationRealRepository extends Repository<HsOfficeRelat
@Query(value = """
SELECT p.* FROM hs_office.relation AS p
WHERE (:relationType IS NULL OR p.type = cast(:relationType AS HsOfficeRelationType))
WHERE (:relationType IS NULL OR p.type = cast(:relationType AS hs_office.RelationType))
AND ( p.anchorUuid = :personUuid OR p.holderUuid = :personUuid)
""", nativeQuery = true)
List<HsOfficeRelationRealEntity> findRelationRelatedToPersonUuidAndRelationTypeString(@NotNull UUID personUuid, String relationType);
@@ -89,7 +89,7 @@ public class InsertTriggerGenerator {
with("superRoleRef", toRoleDescriptor(g.getSuperRoleDef(), "row")));
} else {
plPgSql.writeLn("""
-- Granting INSERT INTO hs_hosting_asset permissions to specified role of pre-existing hs_hosting_asset rows slipped,
-- Granting INSERT INTO hs_hosting.asset permissions to specified role of pre-existing hs_hosting.asset rows slipped,
-- because there cannot yet be any pre-existing rows in the same table yet.
""",
with("rawSuperTable", g.getSuperRoleDef().getEntityAlias().getRawTableNameWithSchema()),
@@ -100,7 +100,7 @@ public class InsertTriggerGenerator {
/**
Grants ${rawSubTable} INSERT permission to specified role of new ${rawSuperTable} rows.
*/
create or replace function ${rawSubTableSchemaPrefix}new_${rawSubTableShortName}_grants_insert_to_${rawSuperTableShortName}_tf()
create or replace function ${rawSubTableSchemaPrefix}${rawSubTableShortName}_grants_insert_to_${rawSuperTableShortName}_tf()
returns trigger
language plpgsql
strict as $$
@@ -113,11 +113,11 @@ public class InsertTriggerGenerator {
return NEW;
end; $$;
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
create trigger z_new_${rawSubTableName}_grants_after_insert_tg
-- ..._z_... is to put it at the end of after insert triggers, to make sure the roles exist
create trigger ${rawSubTableName}_z_grants_after_insert_tg
after insert on ${rawSuperTableWithSchema}
for each row
execute procedure ${rawSubTableSchemaPrefix}new_${rawSubTableShortName}_grants_insert_to_${rawSuperTableShortName}_tf();
execute procedure ${rawSubTableSchemaPrefix}${rawSubTableShortName}_grants_insert_to_${rawSuperTableShortName}_tf();
""",
with("ifConditionThen", g.getSuperRoleDef().getEntityAlias().isCaseDependent()
// TODO.impl: .type needs to be dynamically generated
@@ -325,7 +325,7 @@ public class InsertTriggerGenerator {
private String toRoleDescriptor(final RbacView.RbacRoleDefinition roleDef, final String ref) {
final var functionName = toVar(roleDef);
final var functionName = roleDef.descriptorFunctionName();
if (roleDef.getEntityAlias().isGlobal()) {
return functionName + "()";
}
@@ -19,12 +19,11 @@ public class RbacRoleDescriptorsGenerator {
-- ============================================================================
--changeset RbacRoleDescriptorsGenerator:${liquibaseTagPrefix}-rbac-ROLE-DESCRIPTORS endDelimiter:--//
-- ----------------------------------------------------------------------------
call rbac.generateRbacRoleDescriptors('${simpleEntityVarName}', '${rawTableName}');
call rbac.generateRbacRoleDescriptors('${rawTableName}');
--//
""",
with("liquibaseTagPrefix", liquibaseTagPrefix),
with("simpleEntityVarName", simpleEntityVarName),
with("rawTableName", rawTableName));
}
}
@@ -29,6 +29,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinit
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.Part.AUTO_FETCH;
import static org.apache.commons.collections4.SetUtils.hashSet;
import static org.apache.commons.lang3.StringUtils.capitalize;
import static org.apache.commons.lang3.StringUtils.uncapitalize;
@Getter
@@ -830,6 +831,10 @@ public class RbacView {
public boolean isGlobal(final Role role) {
return entityAlias.isGlobal() && this.role == role;
}
public String descriptorFunctionName() {
return entityAlias.getRawTableNameWithSchema() + "_" + capitalize(role.name());
}
}
public RbacSubjectReference findUserRef(final RbacSubjectReference.UserRole userRole) {
@@ -982,14 +987,12 @@ public class RbacView {
String getRawTableShortName() {
// TODO.impl: some combined function and trigger names are too long
// maybe we should shorten the table name e.g. hs_office.coopsharestransaction -> hsof.coopsharetx
// maybe we should shorten the table name e.g. hs_office.coopsharetx -> hsof.coopsharetx
// this is just a workaround:
return getRawTableName()
.replace("hs_office.", "hsof.")
.replace("hs_booking_", "hsbk_")
.replace("hs_hosting_", "hsho_")
.replace("coopsharestransaction", "coopsharetx")
.replace("coopassetstransaction", "coopassettx");
.replace("hs_booking.", "hsbk_")
.replace("hs_hosting.", "hsho_");
}
String dependsOnColumName() {
@@ -20,7 +20,6 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinit
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with;
import static org.apache.commons.lang3.StringUtils.capitalize;
import static org.apache.commons.lang3.StringUtils.uncapitalize;
class RolesGrantsAndPermissionsGenerator {
@@ -362,11 +361,10 @@ class RolesGrantsAndPermissionsGenerator {
System.out.println("null");
}
if (roleDef.getEntityAlias().isGlobal()) {
return "rbac.globalAdmin()";
return "rbac.global_ADMIN()";
}
final String entityRefVar = entityRefVar(rootRefVar, roleDef.getEntityAlias());
return roleDef.getEntityAlias().simpleName() + capitalize(roleDef.getRole().name())
+ "(" + entityRefVar + ")";
return roleDef.descriptorFunctionName() + "(" + entityRefVar + ")";
}
private String entityRefVar(
@@ -389,8 +387,8 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.writeLn();
plPgSql.writeLn("perform rbac.defineRoleWithGrants(");
plPgSql.indented(() -> {
plPgSql.writeLn("${simpleVarName)${roleSuffix}(NEW),"
.replace("${simpleVarName)", simpleEntityVarName)
plPgSql.writeLn("${qualifiedRawTableName)_${roleSuffix}(NEW),"
.replace("${qualifiedRawTableName)", qualifiedRawTableName)
.replace("${roleSuffix}", capitalize(role.name())));
generatePermissionsForRole(plPgSql, role);
@@ -593,16 +591,12 @@ class RolesGrantsAndPermissionsGenerator {
final RbacView.RbacRoleDefinition roleDef,
final boolean assumed) {
final var assumedArg = assumed ? "" : ", rbac.unassumed()";
return toRoleRef(roleDef) +
return roleDef.descriptorFunctionName() +
(roleDef.getEntityAlias().isGlobal() ? ( assumed ? "()" : "(rbac.unassumed())")
: rbacDef.isRootEntityAlias(roleDef.getEntityAlias()) ? ("(" + triggerRef.name() + ")")
: "(" + toTriggerReference(triggerRef, roleDef.getEntityAlias()) + assumedArg + ")");
}
private static String toRoleRef(final RbacView.RbacRoleDefinition roleDef) {
return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().name());
}
private static String toTriggerReference(
final PostgresTriggerReference triggerRef,
final RbacView.EntityAlias entityAlias) {