@@ -141,6 +141,7 @@ public class ApiConstants { | |||
public static final String EP_MODEL = "/model"; | |||
public static final String EP_VPN = "/vpn"; | |||
public static final String EP_IPS = "/ips"; | |||
public static final String EP_PLAN = "/plan"; | |||
public static final String EP_PAYMENT_METHOD = "/paymentMethod"; | |||
public static final String EP_PAYMENT_METHODS = PAYMENT_METHODS_ENDPOINT; | |||
public static final String EP_PAYMENT = "/payment"; | |||
@@ -1,7 +1,10 @@ | |||
package bubble.dao.bill; | |||
import bubble.dao.account.AccountOwnedEntityDAO; | |||
import bubble.dao.app.BubbleAppDAO; | |||
import bubble.model.app.BubbleApp; | |||
import bubble.model.bill.BubblePlanApp; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Repository; | |||
import java.util.List; | |||
@@ -9,8 +12,18 @@ import java.util.List; | |||
@Repository | |||
public class BubblePlanAppDAO extends AccountOwnedEntityDAO<BubblePlanApp> { | |||
public List<BubblePlanApp> findByAccountAndPlan(String account, String bubblePlan) { | |||
return findByFields("account", account, "plan", bubblePlan); | |||
@Autowired private BubbleAppDAO appDAO; | |||
public List<BubblePlanApp> findByPlan(String bubblePlan) { | |||
return findByField("plan", bubblePlan); | |||
} | |||
public BubblePlanApp findByAccountAndPlanAndId(String account, String bubblePlan, String id) { | |||
final BubblePlanApp planApp = findByUniqueFields("plan", bubblePlan, "app", id); | |||
if (planApp != null) return planApp; | |||
final BubbleApp app = appDAO.findByAccountAndId(account, id); | |||
return app == null ? null : findByUniqueFields("plan", bubblePlan, "app", app.getUuid()); | |||
} | |||
} |
@@ -9,8 +9,8 @@ import lombok.NoArgsConstructor; | |||
import lombok.Setter; | |||
import lombok.experimental.Accessors; | |||
import org.cobbzilla.util.collection.HasPriority; | |||
import org.cobbzilla.wizard.model.IdentifiableBase; | |||
import org.cobbzilla.wizard.model.entityconfig.EntityFieldType; | |||
import org.cobbzilla.wizard.model.entityconfig.IdentifiableBaseParentEntity; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.*; | |||
import org.cobbzilla.wizard.validation.HasValue; | |||
import org.joda.time.format.DateTimeFormat; | |||
@@ -35,7 +35,7 @@ import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | |||
}) | |||
@Entity @NoArgsConstructor @Accessors(chain=true) | |||
@ECIndexes({ @ECIndex(unique=true, of={"account", "name"}) }) | |||
public class BubblePlan extends IdentifiableBase implements HasAccount, HasPriority { | |||
public class BubblePlan extends IdentifiableBaseParentEntity implements HasAccount, HasPriority { | |||
public static final int MAX_CHARGENAME_LEN = 12; | |||
@@ -20,7 +20,9 @@ import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | |||
@ECType(root=true) | |||
@ECTypeURIs(baseURI=EP_APPS, listFields={"plan", "app"}) | |||
@Entity @NoArgsConstructor @Accessors(chain=true) | |||
@ECIndexes({ @ECIndex(unique=true, of={"account", "plan", "app"}) }) | |||
@ECIndexes({ | |||
@ECIndex(unique=true, of={"plan", "app"}) | |||
}) | |||
public class BubblePlanApp extends IdentifiableBase implements HasAccountNoName { | |||
public static final String[] CREATE_FIELDS = {"plan", "app"}; | |||
@@ -256,4 +256,24 @@ public class AccountPlansResource extends AccountOwnedResource<AccountPlan, Acco | |||
return paymentMethod == null ? notFound() : ok(paymentMethod); | |||
} | |||
@GET @Path("/{id}"+EP_PLAN) | |||
public Response getBubblePlan(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final AccountPlan accountPlan = find(ctx, id); | |||
if (accountPlan == null) return notFound(id); | |||
final BubblePlan plan = planDAO.findByUuid(accountPlan.getPlan()); | |||
return plan == null ? notFound() : ok(plan); | |||
} | |||
@Path("/{id}"+EP_APPS) | |||
public BubblePlanAppsResource getApps(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final AccountPlan accountPlan = find(ctx, id); | |||
if (accountPlan == null) throw notFoundEx(id); | |||
final Account account = accountDAO.findByUuid(getAccountUuid(ctx)); | |||
final BubblePlan plan = planDAO.findByUuid(accountPlan.getPlan()); | |||
return configuration.subResource(BubblePlanAppsResource.class, account, plan); | |||
} | |||
} |
@@ -8,9 +8,12 @@ import bubble.model.bill.BubblePlan; | |||
import bubble.model.bill.BubblePlanApp; | |||
import bubble.resources.account.AccountOwnedResource; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.glassfish.grizzly.http.server.Request; | |||
import org.glassfish.jersey.server.ContainerRequest; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import java.util.List; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.notFoundEx; | |||
@Slf4j | |||
@@ -25,6 +28,26 @@ public class BubblePlanAppsResource extends AccountOwnedResource<BubblePlanApp, | |||
@Autowired private BubbleAppDAO appDAO; | |||
@Override protected List<BubblePlanApp> list(ContainerRequest ctx) { | |||
return getDao().findByPlan(plan.getUuid()); | |||
} | |||
@Override protected BubblePlanApp find(ContainerRequest ctx, String id) { | |||
return getDao().findByAccountAndPlanAndId(account.getUuid(), plan.getUuid(), id); | |||
} | |||
@Override protected boolean canCreate(Request req, ContainerRequest ctx, Account caller, BubblePlanApp request) { | |||
return caller.admin(); | |||
} | |||
@Override protected boolean canUpdate(ContainerRequest ctx, Account caller, BubblePlanApp found, BubblePlanApp request) { | |||
return false; | |||
} | |||
@Override protected boolean canDelete(ContainerRequest ctx, Account caller, BubblePlanApp found) { | |||
return caller.admin(); | |||
} | |||
@Override protected BubblePlanApp setReferences(ContainerRequest ctx, Account caller, BubblePlanApp request) { | |||
final BubbleApp app = appDAO.findByAccountAndId(getAccountUuid(ctx), request.getApp()); | |||
if (app == null) throw notFoundEx(request.getApp()); | |||
@@ -16,8 +16,7 @@ import static bubble.ApiConstants.EP_APPS; | |||
import static bubble.ApiConstants.PLANS_ENDPOINT; | |||
import static bubble.model.bill.BubblePlan.MAX_CHARGENAME_LEN; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.notFoundEx; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | |||
@Path(PLANS_ENDPOINT) | |||
@Service @Slf4j | |||
@@ -36,7 +35,8 @@ public class BubblePlansResource extends AccountOwnedResource<BubblePlan, Bubble | |||
@PathParam("id") String id) { | |||
final BubblePlan plan = find(ctx, id); | |||
if (plan == null) throw notFoundEx(id); | |||
return configuration.subResource(BubblePlanAppsResource.class, account, plan); | |||
final Account caller = userPrincipal(ctx); | |||
return configuration.subResource(BubblePlanAppsResource.class, caller, plan); | |||
} | |||
} |
@@ -90,7 +90,7 @@ public class AnsiblePrepService { | |||
if (configuration.paymentsEnabled()) { | |||
final AccountPlan accountPlan = accountPlanDAO.findByAccountAndNetwork(account.getUuid(), network.getUuid()); | |||
if (accountPlan == null) return die("prepAnsible: no AccountPlan found for network: "+network.getUuid()); | |||
planApps = planAppDAO.findByAccountAndPlan(account.getUuid(), accountPlan.getPlan()); | |||
planApps = planAppDAO.findByPlan(accountPlan.getPlan()); | |||
} else { | |||
planApps = null; | |||
} | |||
@@ -14,4 +14,6 @@ public class PaymentTest extends PaymentTestBase { | |||
modelTest("payment/pay_credit_refund_and_restart"); | |||
} | |||
@Test public void testAppsForPlan () throws Exception { modelTest("payment/plan_apps"); } | |||
} |
@@ -2,11 +2,11 @@ | |||
"system/cloudService", | |||
"system/cloudService_test", | |||
"system/bubbleDomain", | |||
"system/bubblePlan", | |||
"system/bubbleFootprint", | |||
"system/ruleDriver", | |||
"system/account_testDevice", | |||
"manifest-app-analytics", | |||
"manifest-app-user-block-hn", | |||
"manifest-app-user-block-localhost" | |||
"manifest-app-user-block-localhost", | |||
"system/bubblePlan_withApps" | |||
] |
@@ -1,23 +1,26 @@ | |||
[{ | |||
"name": "bubble", | |||
"chargeName": "BubbleVPNP", | |||
"computeSizeType": "small", | |||
"nodesIncluded": 1, | |||
"additionalPerNodePrice": 1200, | |||
"price": 1200, | |||
"storageGbIncluded": 15, | |||
"additionalStoragePerGbPrice": 2, | |||
"bandwidthGbIncluded": 500, | |||
"additionalBandwidthPerGbPrice": 2 | |||
}, { | |||
"name": "bubble_plus", | |||
"chargeName": "BubblePlus", | |||
"computeSizeType": "medium", | |||
"nodesIncluded": 1, | |||
"additionalPerNodePrice": 1900, | |||
"price": 1900, | |||
"storageGbIncluded": 40, | |||
"additionalStoragePerGbPrice": 2, | |||
"bandwidthGbIncluded": 1000, | |||
"additionalBandwidthPerGbPrice": 2 | |||
}] | |||
[ | |||
{ | |||
"name": "bubble", | |||
"chargeName": "BubbleVPNP", | |||
"computeSizeType": "small", | |||
"nodesIncluded": 1, | |||
"additionalPerNodePrice": 1200, | |||
"price": 1200, | |||
"storageGbIncluded": 15, | |||
"additionalStoragePerGbPrice": 2, | |||
"bandwidthGbIncluded": 500, | |||
"additionalBandwidthPerGbPrice": 2 | |||
}, | |||
{ | |||
"name": "bubble_plus", | |||
"chargeName": "BubblePlus", | |||
"computeSizeType": "medium", | |||
"nodesIncluded": 1, | |||
"additionalPerNodePrice": 1900, | |||
"price": 1900, | |||
"storageGbIncluded": 40, | |||
"additionalStoragePerGbPrice": 2, | |||
"bandwidthGbIncluded": 1000, | |||
"additionalBandwidthPerGbPrice": 2 | |||
} | |||
] |
@@ -0,0 +1,37 @@ | |||
[ | |||
{ | |||
"name": "bubble", | |||
"chargeName": "BubbleVPNP", | |||
"computeSizeType": "small", | |||
"nodesIncluded": 1, | |||
"additionalPerNodePrice": 1200, | |||
"price": 1200, | |||
"storageGbIncluded": 15, | |||
"additionalStoragePerGbPrice": 2, | |||
"bandwidthGbIncluded": 500, | |||
"additionalBandwidthPerGbPrice": 2, | |||
"children": { | |||
"BubblePlanApp": [ | |||
{"app": "UserBlocker"} | |||
] | |||
} | |||
}, | |||
{ | |||
"name": "bubble_plus", | |||
"chargeName": "BubblePlus", | |||
"computeSizeType": "medium", | |||
"nodesIncluded": 1, | |||
"additionalPerNodePrice": 1900, | |||
"price": 1900, | |||
"storageGbIncluded": 40, | |||
"additionalStoragePerGbPrice": 2, | |||
"bandwidthGbIncluded": 1000, | |||
"additionalBandwidthPerGbPrice": 2, | |||
"children": { | |||
"BubblePlanApp": [ | |||
{"app": "TrafficAnalytics"}, | |||
{"app": "UserBlocker"} | |||
] | |||
} | |||
} | |||
] |
@@ -0,0 +1,104 @@ | |||
[ | |||
{ | |||
"comment": "create a user account", | |||
"request": { | |||
"uri": "users", | |||
"method": "put", | |||
"entity": { | |||
"name": "test_user_free", | |||
"password": "password", | |||
"contact": {"type": "email", "info": "test-user@example.com"} | |||
} | |||
} | |||
}, | |||
{ | |||
"before": "sleep 22s", // wait for account objects to be created | |||
"comment": "login as new user", | |||
"request": { | |||
"session": "new", | |||
"uri": "auth/login", | |||
"entity": { | |||
"name": "test_user_free", | |||
"password": "password" | |||
} | |||
}, | |||
"response": { | |||
"store": "testAccount", | |||
"sessionName": "userSession", | |||
"session": "token" | |||
} | |||
}, | |||
{ | |||
"comment": "get plans", | |||
"request": { "uri": "plans" }, | |||
"response": { | |||
"store": "plans", | |||
"check": [{"condition": "json.length >= 1"}] | |||
} | |||
}, | |||
{ | |||
"comment": "add basic plan, using 'free' payment method", | |||
"request": { | |||
"uri": "me/plans", | |||
"method": "put", | |||
"entity": { | |||
"name": "test-net-{{rand 5}}", | |||
"domain": "{{defaultDomain}}", | |||
"locale": "en_US", | |||
"timezone": "EST", | |||
"plan": "{{plans.[0].name}}", | |||
"footprint": "US", | |||
"paymentMethodObject": { | |||
"paymentMethodType": "free", | |||
"paymentInfo": "free" | |||
} | |||
} | |||
}, | |||
"response": { | |||
"store": "accountPlan" | |||
} | |||
}, | |||
{ | |||
"comment": "get plan apps, should be 1", | |||
"request": { "uri": "me/plans/{{accountPlan.uuid}}/apps" }, | |||
"response": { | |||
"check": [{"condition": "json.length == 1"}] | |||
} | |||
}, | |||
{ | |||
"comment": "add plus plan, using 'free' payment method", | |||
"request": { | |||
"uri": "me/plans", | |||
"method": "put", | |||
"entity": { | |||
"name": "test-net-{{rand 5}}", | |||
"domain": "{{defaultDomain}}", | |||
"locale": "en_US", | |||
"timezone": "EST", | |||
"plan": "{{plans.[1].name}}", | |||
"footprint": "US", | |||
"paymentMethodObject": { | |||
"paymentMethodType": "free", | |||
"paymentInfo": "free" | |||
} | |||
} | |||
}, | |||
"response": { | |||
"store": "accountPlan2" | |||
} | |||
}, | |||
{ | |||
"comment": "get plan apps, should be 2", | |||
"request": { "uri": "me/plans/{{accountPlan2.uuid}}/apps" }, | |||
"response": { | |||
"check": [{"condition": "json.length == 2"}] | |||
} | |||
} | |||
] |