Bläddra i källkod

fix setup of BubblePlanApps for new node

tags/v0.5.2
Jonathan Cobb 5 år sedan
förälder
incheckning
f1295c403f
13 ändrade filer med 146 tillägg och 30 borttagningar
  1. +3
    -0
      bubble-server/src/main/java/bubble/dao/account/AccountDAO.java
  2. +4
    -0
      bubble-server/src/main/java/bubble/dao/app/BubbleAppDAO.java
  3. +7
    -6
      bubble-server/src/main/java/bubble/dao/bill/BubblePlanAppDAO.java
  4. +8
    -0
      bubble-server/src/main/java/bubble/dao/bill/BubblePlanDAO.java
  5. +2
    -1
      bubble-server/src/main/java/bubble/model/account/Account.java
  6. +12
    -3
      bubble-server/src/main/java/bubble/model/app/BubbleApp.java
  7. +1
    -2
      bubble-server/src/main/java/bubble/model/bill/BubblePlan.java
  8. +3
    -0
      bubble-server/src/main/java/bubble/model/bill/BubblePlanApp.java
  9. +12
    -5
      bubble-server/src/main/java/bubble/resources/bill/BubblePlanAppsResource.java
  10. +26
    -6
      bubble-server/src/main/java/bubble/resources/bill/BubblePlansResource.java
  11. +38
    -2
      bubble-server/src/main/java/bubble/service/dbfilter/EntityIterator.java
  12. +29
    -4
      bubble-server/src/test/resources/models/tests/payment/plan_apps.json
  13. +1
    -1
      utils/cobbzilla-wizard

+ 3
- 0
bubble-server/src/main/java/bubble/dao/account/AccountDAO.java Visa fil

@@ -206,6 +206,9 @@ public class AccountDAO extends AbstractCRUDDAO<Account> implements SqlViewSearc

final Map<String, BubbleApp> apps = new HashMap<>();
copyTemplateObjects(acct, parent, appDAO, new AccountTemplate.CopyTemplate<>() {
@Override public BubbleApp preCreate(BubbleApp parentApp, BubbleApp accountApp) {
return accountApp.setTemplateApp(parentApp.getUuid());
}
@Override public void postCreate(BubbleApp parentApp, BubbleApp accountApp) {
apps.put(parentApp.getUuid(), accountApp);
}


+ 4
- 0
bubble-server/src/main/java/bubble/dao/app/BubbleAppDAO.java Visa fil

@@ -25,4 +25,8 @@ public class BubbleAppDAO extends AccountOwnedTemplateDAO<BubbleApp> {
super.delete(uuid);
}

public BubbleApp findByAccountAndTemplateApp(String accountUuid, String templateAppUuid) {
return findByUniqueFields("account", accountUuid, "templateApp", templateAppUuid);
}

}

+ 7
- 6
bubble-server/src/main/java/bubble/dao/bill/BubblePlanAppDAO.java Visa fil

@@ -3,6 +3,7 @@ package bubble.dao.bill;
import bubble.dao.account.AccountOwnedEntityDAO;
import bubble.dao.app.BubbleAppDAO;
import bubble.model.app.BubbleApp;
import bubble.model.bill.BubblePlan;
import bubble.model.bill.BubblePlanApp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@@ -16,16 +17,16 @@ public class BubblePlanAppDAO extends AccountOwnedEntityDAO<BubblePlanApp> {

@Override public boolean dbFilterIncludeAll() { return true; }

public List<BubblePlanApp> findByPlan(String bubblePlan) {
return findByField("plan", bubblePlan);
public List<BubblePlanApp> findByPlan(String bubblePlanUuid) {
return findByField("plan", bubblePlanUuid);
}

public BubblePlanApp findByAccountAndPlanAndId(String account, String bubblePlan, String id) {
final BubblePlanApp planApp = findByUniqueFields("plan", bubblePlan, "app", id);
public BubblePlanApp findByPlanAndId(BubblePlan plan, String id) {
final BubblePlanApp planApp = findByUniqueFields("plan", plan.getUuid(), "app", id);
if (planApp != null) return planApp;

final BubbleApp app = appDAO.findByAccountAndId(account, id);
return app == null ? null : findByUniqueFields("plan", bubblePlan, "app", app.getUuid());
final BubbleApp app = appDAO.findByAccountAndId(plan.getAccount(), id);
return app == null ? null : findByUniqueFields("plan", plan.getUuid(), "app", app.getUuid());
}

}

+ 8
- 0
bubble-server/src/main/java/bubble/dao/bill/BubblePlanDAO.java Visa fil

@@ -30,4 +30,12 @@ public class BubblePlanDAO extends AccountOwnedEntityDAO<BubblePlan> {
return null;
}

public BubblePlan findByName(String name) { return findByUniqueField("name", name); }

public BubblePlan findById(String id) {
final BubblePlan plan = findByUuid(id);
if (plan != null) return plan;
return findByName(id);
}

}

+ 2
- 1
bubble-server/src/main/java/bubble/model/account/Account.java Visa fil

@@ -115,7 +115,8 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci

// make this updatable if we ever want accounts to be able to change parents
// there might be a lot more involved in that action though (read-only parent objects that will no longer be visible, must be copied in?)
@ECIndex @Column(length=UUID_MAXLEN, updatable=false) @ECField(index=20, mode=EntityFieldMode.readOnly)
@ECForeignKey(entity=Account.class) @ECField(index=20, mode=EntityFieldMode.readOnly)
@Column(length=UUID_MAXLEN, updatable=false)
@Getter @Setter private String parent;
public boolean hasParent () { return parent != null; }



+ 12
- 3
bubble-server/src/main/java/bubble/model/app/BubbleApp.java Visa fil

@@ -39,6 +39,7 @@ import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENC_PAD;
@Entity @NoArgsConstructor @Accessors(chain=true)
@ECIndexes({
@ECIndex(unique=true, of={"account", "name"}),
@ECIndex(unique=true, of={"account", "templateApp"}),
@ECIndex(of={"account", "template", "enabled"}),
@ECIndex(of={"template", "enabled"})
})
@@ -93,15 +94,23 @@ public class BubbleApp extends IdentifiableBaseParentEntity implements AccountTe
return adc;
}

@ECSearchable @ECField(index=60)
// We do NOT add @ECForeignKey here, since the template BubbleApp will not be copied
// to a new node. This App will become a template/root BubbleApp for a new node, if it
// is owned by a user and applicable to the BubblePlan (via BubblePlanApp)
// For system apps, this can be null
@ECField(index=60)
@Column(length=UUID_MAXLEN, updatable=false)
@Getter @Setter private String templateApp;

@ECSearchable @ECField(index=70)
@ECIndex @Column(nullable=false)
@Getter @Setter private Boolean template = false;

@ECSearchable @ECField(index=70)
@ECSearchable @ECField(index=80)
@ECIndex @Column(nullable=false)
@Getter @Setter private Boolean enabled = true;

@ECSearchable @ECField(index=80)
@ECSearchable @ECField(index=90)
@ECIndex @Getter @Setter private Boolean needsUpdate = false;

}

+ 1
- 2
bubble-server/src/main/java/bubble/model/bill/BubblePlan.java Visa fil

@@ -35,7 +35,6 @@ import static org.cobbzilla.util.reflect.ReflectionUtil.copy;
@ECTypeChild(type=BubblePlanApp.class, backref="plan")
})
@Entity @NoArgsConstructor @Accessors(chain=true)
@ECIndexes({ @ECIndex(unique=true, of={"account", "name"}) })
public class BubblePlan extends IdentifiableBaseParentEntity implements HasAccount, HasPriority {

public static final int MAX_CHARGENAME_LEN = 12;
@@ -55,7 +54,7 @@ public class BubblePlan extends IdentifiableBaseParentEntity implements HasAccou

@ECSearchable(filter=true) @ECField(index=10)
@HasValue(message="err.name.required")
@ECIndex @Column(nullable=false, updatable=false, length=200)
@ECIndex(unique=true) @Column(nullable=false, updatable=false, length=200)
@Getter @Setter private String name;

@ECSearchable @ECField(index=20)


+ 3
- 0
bubble-server/src/main/java/bubble/model/bill/BubblePlanApp.java Visa fil

@@ -13,6 +13,7 @@ import org.cobbzilla.wizard.model.entityconfig.annotations.*;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Transient;

import static bubble.ApiConstants.EP_APPS;
import static org.cobbzilla.util.reflect.ReflectionUtil.copy;
@@ -47,4 +48,6 @@ public class BubblePlanApp extends IdentifiableBase implements HasAccountNoName
@Column(nullable=false, updatable=false, length=UUID_MAXLEN)
@Getter @Setter private String app;

@Transient @Getter @Setter private transient BubbleApp appObject;

}

+ 12
- 5
bubble-server/src/main/java/bubble/resources/bill/BubblePlanAppsResource.java Visa fil

@@ -28,12 +28,19 @@ public class BubblePlanAppsResource extends AccountOwnedResource<BubblePlanApp,

@Autowired private BubbleAppDAO appDAO;

@Override protected List<BubblePlanApp> list(ContainerRequest ctx) {
return getDao().findByPlan(plan.getUuid());
}
@Override protected List<BubblePlanApp> list(ContainerRequest ctx) { return getDao().findByPlan(plan.getUuid()); }
@Override protected BubblePlanApp find(ContainerRequest ctx, String id) { return getDao().findByPlanAndId(plan, id); }

@Override protected BubblePlanApp find(ContainerRequest ctx, String id) {
return getDao().findByAccountAndPlanAndId(account.getUuid(), plan.getUuid(), id);
@Override protected BubblePlanApp populate(ContainerRequest ctx, BubblePlanApp planApp) {
final BubbleApp globalApp = appDAO.findByAccountAndId(planApp.getAccount(), planApp.getApp());
if (globalApp == null) {
log.warn("populate: globalApp "+planApp.getApp()+" not found for planApp: "+planApp.getUuid());
} else {
final BubbleApp userApp = appDAO.findByAccountAndTemplateApp(getAccountUuid(ctx), globalApp.getUuid());
planApp.setAppObject(userApp);
}
return super.populate(ctx, planApp);
}

@Override protected boolean canCreate(Request req, ContainerRequest ctx, Account caller, BubblePlanApp request) {


+ 26
- 6
bubble-server/src/main/java/bubble/resources/bill/BubblePlansResource.java Visa fil

@@ -10,6 +10,7 @@ import bubble.model.bill.BubblePlanApp;
import bubble.resources.account.AccountOwnedResource;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.util.collection.ExpirationMap;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.jersey.server.ContainerRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -42,6 +43,31 @@ public class BubblePlansResource extends AccountOwnedResource<BubblePlan, Bubble
return super.setReferences(ctx, caller, bubblePlan);
}

// BubblePlan objects are global, no need to qualify by account
@Override protected BubblePlan find(ContainerRequest ctx, String id) { return getDao().findById(id); }
@Override protected List<BubblePlan> list(ContainerRequest ctx) { return getDao().findAll(); }

// only admins can create
@Override protected boolean canCreate(Request req, ContainerRequest ctx, Account caller, BubblePlan request) {
return caller.admin();
}

// only owner can edit
@Override protected boolean canUpdate(ContainerRequest ctx, Account caller, BubblePlan found, BubblePlan request) {
return caller.admin() && caller.getUuid().equals(found.getAccount());
}

// only owner can delete
@Override protected boolean canDelete(ContainerRequest ctx, Account caller, BubblePlan found) {
return caller.admin() && caller.getUuid().equals(found.getAccount());
}

@Override protected BubblePlan populate(ContainerRequest ctx, BubblePlan plan) {
final List<BubbleApp> apps = getAppsForPlan(plan);
plan.setApps(apps);
return super.populate(ctx, plan);
}

@Path("/{id}"+EP_APPS)
public BubblePlanAppsResource getApps(@Context ContainerRequest ctx,
@PathParam("id") String id) {
@@ -51,12 +77,6 @@ public class BubblePlansResource extends AccountOwnedResource<BubblePlan, Bubble
return configuration.subResource(BubblePlanAppsResource.class, caller, plan);
}

@Override protected BubblePlan populate(ContainerRequest ctx, BubblePlan plan) {
final List<BubbleApp> apps = getAppsForPlan(plan);
plan.setApps(apps);
return super.populate(ctx, plan);
}

private Map<String, List<BubbleApp>> appCache = new ExpirationMap<>();

private List<BubbleApp> getAppsForPlan(BubblePlan plan) {


+ 38
- 2
bubble-server/src/main/java/bubble/service/dbfilter/EntityIterator.java Visa fil

@@ -10,6 +10,7 @@ import bubble.model.cloud.BubbleNetwork;
import bubble.model.cloud.BubbleNode;
import bubble.model.cloud.CloudService;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.wizard.model.Identifiable;

import java.util.Iterator;
@@ -23,7 +24,9 @@ import static bubble.cloud.storage.local.LocalStorageDriver.LOCAL_STORAGE_STANDA
import static bubble.service.dbfilter.EndOfEntityStream.END_OF_ENTITY_STREAM;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.json.JsonUtil.json;
import static org.cobbzilla.wizard.model.NamedEntity.names;

@Slf4j
public abstract class EntityIterator implements Iterator<Identifiable> {

private static final int MAX_QUEUE_SIZE = 100;
@@ -32,6 +35,7 @@ public abstract class EntityIterator implements Iterator<Identifiable> {

@Getter private final Thread thread;
@Getter private final AtomicReference<Exception> error;
private List<BubbleApp> userApps;

public EntityIterator(AtomicReference<Exception> error) {
this.error = error;
@@ -84,14 +88,46 @@ public abstract class EntityIterator implements Iterator<Identifiable> {
entities.forEach(e -> add(setInstallKey((AccountSshKey) e, network)));

} else if (planApps != null && BubbleApp.class.isAssignableFrom(c)) {
// only copy enabled apps
// only copy enabled apps, make them templates
entities.stream().filter(e -> planAppEnabled(e.getUuid(), planApps))
.map(app -> ((BubbleApp) app).setTemplate(true))
.forEach(this::add);

// save these for later, we will need them when copying BubblePlanApps below
userApps = (List<BubbleApp>) entities;

} else if (planApps != null && BubblePlanApp.class.isAssignableFrom(c)) {
// the only BubblePlanApps we will see here are the ones associated with the system BubblePlans
// and the system/template BubbleApps.
// But for this new node, the BubbleApps that are associated with the first user (admin of new node)
// will become the new system/template apps.
// So we rewrite the "app" field to refer to the BubbleApp owned by the user.

// Unless for some odd reason we are deploying a node with NO apps, in which case we can skip this section entirely
if (planApps.isEmpty()) {
log.warn("addEntities: no BubblePlanApps enabled, none will be copied to new node");

} else {
for (Identifiable e : entities) {
final BubblePlanApp systemPlanApp = (BubblePlanApp) e;
final BubbleApp userApp = userApps.stream()
.filter(app -> app.getTemplateApp().equals(systemPlanApp.getApp()))
.findFirst().orElse(null);
if (userApp == null) {
log.info("addEntities: system BubblePlanApp " + systemPlanApp.getName() + " not found in userApps (not adding): " + names(userApps));
} else {
// systemPlanApp will now be associated with "root"'s BubblePlan, but user's BubbleApp
log.info("addEntities: rewrite ");
systemPlanApp.setApp(userApp.getUuid());
}
}
}

} else if (planApps != null && AppTemplateEntity.class.isAssignableFrom(c)) {
// only copy app-related entities for enabled apps
// only copy app-related entities for enabled apps, make them all templates
entities.stream()
.filter(e -> planAppEnabled(((AppTemplateEntity) e).getApp(), planApps))
.map(app -> (AppTemplateEntity) ((AppTemplateEntity) app).setTemplate(true))
.forEach(this::add);

} else {


+ 29
- 4
bubble-server/src/test/resources/models/tests/payment/plan_apps.json Visa fil

@@ -35,7 +35,25 @@
"request": { "uri": "plans" },
"response": {
"store": "plans",
"check": [{"condition": "json.length >= 1"}]
"check": [
{"condition": "json.length >= 1"},
{"condition": "json[0].getApps().length >= 1"}
]
}
},

{
"comment": "get plan apps for a system plan, we actually see our apps underneath",
"request": { "uri": "plans/{{plans.[0].name}}/apps" },
"response": {
"store": "planApps",
"check": [
{"condition": "json.length === 1"},
{"condition": "json[0].getAppObject().getTemplateApp() !== null"},
{"condition": "json[0].getAppObject().template() === false"},
{"condition": "json[0].getAppObject().getUuid() !== json[0].getApp()"},
{"condition": "json[0].getAppObject().getTemplateApp() === json[0].getApp()"}
]
}
},

@@ -63,10 +81,17 @@
},

{
"comment": "get plan apps, should be 1",
"comment": "get plan apps, should be 1 and should have templateApp",
"request": { "uri": "me/plans/{{accountPlan.uuid}}/apps" },
"response": {
"check": [{"condition": "json.length == 1"}]
"check": [
{"condition": "json.length === 1"},
{"condition": "json[0].getAppObject().getTemplateApp() !== null"},
{"condition": "json[0].getAppObject().getName() === 'UserBlocker'"},
{"condition": "json[0].getAppObject().getUuid() !== json[0].getApp()"},
{"condition": "json[0].getAppObject().getTemplateApp() === json[0].getApp()"},
{"condition": "json[0].getAppObject().template() === false"}
]
}
},

@@ -97,7 +122,7 @@
"comment": "get plan apps, should be 2",
"request": { "uri": "me/plans/{{accountPlan2.uuid}}/apps" },
"response": {
"check": [{"condition": "json.length == 2"}]
"check": [{"condition": "json.length === 2"}]
}
}


+ 1
- 1
utils/cobbzilla-wizard

@@ -1 +1 @@
Subproject commit 88f19d61844a4fd7bce11f81021e798473b9f135
Subproject commit 7ce51555339f93ec769066bf66b1a2831f298b22

Laddar…
Avbryt
Spara