@@ -31,6 +31,8 @@ import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||
@Slf4j | |||
public class ApiConstants { | |||
public static final int MAX_SEARCH_PAGE = 50; | |||
@Getter(lazy=true) private static final String bubbleDefaultDomain = initDefaultDomain(); | |||
private static String initDefaultDomain() { | |||
final File f = new File(HOME_DIR, ".BUBBLE_DEFAULT_DOMAIN"); | |||
@@ -149,14 +151,14 @@ public class ApiConstants { | |||
public static final String EP_RESTORE = "/restore"; | |||
public static final String EP_KEYS = "/keys"; | |||
public static final String EP_STATUS = "/status"; | |||
public static final String EP_ID = "/id"; | |||
public static final String EP_SEARCH = "/search"; | |||
public static final String EP_FORK = "/fork"; | |||
public static final String DETECT_ENDPOINT = "/detect"; | |||
public static final String EP_LOCALE = "/locale"; | |||
public static final String EP_TIMEZONE = "/timezone"; | |||
public static final String ID_ENDPOINT = "/id"; | |||
public static final String SEARCH_ENDPOINT = "/search"; | |||
public static final String DEBUG_ENDPOINT = "/debug"; | |||
public static final String BUBBLE_MAGIC_ENDPOINT = "/.bubble"; | |||
public static final String EP_ASSETS = "/assets"; | |||
@@ -26,7 +26,6 @@ import static org.cobbzilla.util.daemon.ZillaRuntime.die; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.hashOf; | |||
import static org.cobbzilla.util.io.FileUtil.*; | |||
import static org.cobbzilla.util.json.JsonUtil.json; | |||
import static org.cobbzilla.util.security.ShaUtil.sha256_hex; | |||
import static org.cobbzilla.wizard.cache.redis.RedisService.EX; | |||
@Slf4j | |||
@@ -75,7 +74,7 @@ public abstract class GeoLocateServiceDriverBase<T> extends CloudServiceDriverBa | |||
final HttpRequestBean request = new HttpRequestBean(urlWithLicense).setHeaders(headers); | |||
final HttpMeta meta = HttpUtil.getHeadMetadata(request); | |||
final String uniq = sha256_hex(hashOf(url, headers)); | |||
final String uniq = hashOf(url, headers); | |||
final String dbKey = "dbcache_" + uniq; | |||
final File dbFile = cloudDataDAO.getFile(cloud.getUuid(), dbKey); | |||
if (!meta.shouldRefresh(dbFile)) return dbFile; // we are current! | |||
@@ -41,6 +41,7 @@ import static org.cobbzilla.util.system.Sleep.sleep; | |||
import static org.cobbzilla.util.time.TimeUtil.formatDuration; | |||
import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENCRYPTED_STRING; | |||
import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENC_PAD; | |||
import static org.cobbzilla.wizard.model.entityconfig.annotations.ECForeignKeySearchDepth.none; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||
@ECType(root=true) @ECTypeURIs(listFields={"name", "url", "description", "admin", "suspended"}, isDeleteDefined=false) | |||
@@ -56,6 +57,7 @@ import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||
@ECTypeChild(type=BubbleNode.class, backref="account"), | |||
@ECTypeChild(type=SentNotification.class, backref="account") | |||
}) | |||
@ECSearchDepth(fkDepth=none) | |||
@Entity @NoArgsConstructor @Accessors(chain=true) @Slf4j | |||
public class Account extends IdentifiableBase implements TokenPrincipal, SqlViewSearchResult { | |||
@@ -11,10 +11,7 @@ import lombok.ToString; | |||
import lombok.experimental.Accessors; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.wizard.model.IdentifiableBase; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECForeignKey; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECIndex; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECIndexes; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECType; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.*; | |||
import org.hibernate.annotations.Type; | |||
import javax.persistence.*; | |||
@@ -49,21 +46,26 @@ public class AccountMessage extends IdentifiableBase implements HasAccount { | |||
public boolean hasContact () { return !empty(contact); } | |||
public boolean isSameContact (String uuid) { return hasContact() && contact.equals(uuid); } | |||
@ECSearchable(filter=true) | |||
@ECIndex @Column(length=UUID_MAXLEN, nullable=false, updatable=false) | |||
@Getter @Setter private String name; | |||
@ECSearchable(filter=true) | |||
@Size(max=100, message="err.remoteHost.length") | |||
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(100+ENC_PAD)+")") | |||
@Getter @Setter private String remoteHost; | |||
@ECSearchable | |||
@Enumerated(EnumType.STRING) | |||
@ECIndex @Column(length=20, nullable=false, updatable=false) | |||
@Getter @Setter private AccountMessageType messageType; | |||
@ECSearchable | |||
@Enumerated(EnumType.STRING) | |||
@ECIndex @Column(length=20, nullable=false, updatable=false) | |||
@Getter @Setter private AccountAction action; | |||
@ECSearchable | |||
@Enumerated(EnumType.STRING) | |||
@ECIndex @Column(length=20, nullable=false, updatable=false) | |||
@Getter @Setter private ActionTarget target; | |||
@@ -9,6 +9,7 @@ import lombok.experimental.Accessors; | |||
import org.cobbzilla.util.collection.ArrayUtil; | |||
import org.cobbzilla.wizard.model.Identifiable; | |||
import org.cobbzilla.wizard.model.IdentifiableBase; | |||
import org.cobbzilla.wizard.model.entityconfig.EntityFieldType; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.*; | |||
import org.cobbzilla.wizard.validation.HasValue; | |||
import org.hibernate.annotations.Type; | |||
@@ -46,30 +47,36 @@ public class AppData extends IdentifiableBase implements AppTemplateEntity { | |||
@Override @Transient public String getName() { return getKey(); } | |||
public AppData setName(String n) { return setKey(n); } | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable | |||
@ECForeignKey(entity=BubbleApp.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String app; | |||
public boolean hasApp () { return app != null; } | |||
@ECSearchable | |||
@ECForeignKey(entity=AppMatcher.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String matcher; | |||
public boolean hasMatcher() { return matcher != null; } | |||
@ECSearchable | |||
@ECForeignKey(entity=AppSite.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String site; | |||
public boolean hasSite() { return site != null; } | |||
@ECSearchable(filter=true) | |||
@HasValue(message="err.key.required") | |||
@ECIndex @Column(nullable=false, updatable=false, length=5000) | |||
@Getter @Setter private String key; | |||
public boolean hasKey () { return key != null; } | |||
@ECSearchable(filter=true) | |||
@Size(max=100000, message="err.data.length") | |||
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(100000+ENC_PAD)+")") | |||
@Getter @Setter private String data; | |||
@@ -84,12 +91,15 @@ public class AppData extends IdentifiableBase implements AppTemplateEntity { | |||
return setData(String.valueOf(val+1)); | |||
} | |||
@ECSearchable(type=EntityFieldType.expiration_time) | |||
@ECIndex @Getter @Setter private Long expiration; | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean template = false; | |||
public boolean template() { return template != null && template; } | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean enabled = true; | |||
public boolean enabled() { return enabled != null && enabled; } | |||
@@ -40,26 +40,32 @@ public class AppMatcher extends IdentifiableBase implements AppTemplateEntity { | |||
public static final String[] VALUE_FIELDS = {"fqdn", "urlRegex", "rule", "template", "enabled"}; | |||
public static final String[] CREATE_FIELDS = ArrayUtil.append(VALUE_FIELDS, "name", "site"); | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable(filter=true) | |||
@ECIndex @Column(nullable=false, updatable=false, length=200) | |||
@Getter @Setter private String name; | |||
@ECSearchable | |||
@ECForeignKey(entity=BubbleApp.class) | |||
@Column(nullable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String app; | |||
@ECSearchable | |||
@ECForeignKey(entity=AppSite.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String site; | |||
@ECSearchable(filter=true) | |||
@HasValue(message="err.fqdn.required") | |||
@Size(max=1024, message="err.fqdn.length") | |||
@ECIndex @Column(nullable=false, length=1024) | |||
@Getter @Setter private String fqdn; | |||
@ECSearchable(filter=true) | |||
@HasValue(message="err.urlRegex.required") | |||
@Size(max=1024, message="err.urlRegex.length") | |||
@Type(type=ENCRYPTED_STRING) @Column(nullable=false, columnDefinition="varchar("+(1024+ENC_PAD)+") UNIQUE") | |||
@@ -69,18 +75,22 @@ public class AppMatcher extends IdentifiableBase implements AppTemplateEntity { | |||
public boolean matches (String value) { return getPattern().matcher(value).find(); } | |||
@ECSearchable | |||
@ECForeignKey(entity=AppRule.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String rule; | |||
@ECSearchable | |||
@Column(nullable=false) | |||
@Getter @Setter private Boolean blocked = false; | |||
public boolean blocked() { return blocked != null && blocked; } | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean template = false; | |||
public boolean template () { return template == null || template; } | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean enabled = true; | |||
public boolean enabled () { return enabled == null || enabled; } | |||
@@ -46,10 +46,12 @@ public class AppRule extends IdentifiableBaseParentEntity implements AppTemplate | |||
public static final String[] VALUE_FIELDS = {"driver", "configJson", "template", "enabled"}; | |||
public static final String[] CREATE_FIELDS = ArrayUtil.append(VALUE_FIELDS, "name"); | |||
@ECSearchable(filter=true) | |||
@HasValue(message="err.name.required") | |||
@ECIndex @Column(nullable=false, updatable=false, length=200) | |||
@Getter @Setter private String name; | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@@ -58,21 +60,26 @@ public class AppRule extends IdentifiableBaseParentEntity implements AppTemplate | |||
return (account == null && accountUuid == null) || (account != null && account.equals(accountUuid)); | |||
} | |||
@ECSearchable | |||
@ECForeignKey(entity=BubbleApp.class) | |||
@Column(nullable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String app; | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean template = false; | |||
public boolean template () { return template == null || template; } | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean enabled = true; | |||
public boolean enabled () { return enabled == null || enabled; } | |||
@ECSearchable | |||
@Column(nullable=false) | |||
@Getter @Setter private Integer priority = 0; | |||
@ECSearchable | |||
@ECForeignKey(entity=RuleDriver.class) | |||
@HasValue(message="err.driver.required") | |||
@Column(nullable=false, length=UUID_MAXLEN) | |||
@@ -84,6 +91,7 @@ public class AppRule extends IdentifiableBaseParentEntity implements AppTemplate | |||
return d; | |||
} | |||
@ECSearchable(filter=true) | |||
@Size(max=500000, message="err.configJson.length") | |||
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(500000+ENC_PAD)+")") | |||
@JsonIgnore @Getter @Setter private String configJson; | |||
@@ -42,28 +42,35 @@ public class AppSite extends IdentifiableBase implements AppTemplateEntity { | |||
return this; | |||
} | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable(filter=true) | |||
@ECIndex @Column(nullable=false, updatable=false, length=1000) | |||
@Getter @Setter private String name; | |||
@ECSearchable | |||
@ECForeignKey(entity=BubbleApp.class) | |||
@Column(nullable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String app; | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean template = false; | |||
public boolean template () { return template == null || template; } | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean enabled = true; | |||
public boolean enabled () { return enabled == null || enabled; } | |||
@ECSearchable(filter=true) | |||
@Column(nullable=false, length=10000) | |||
@Getter @Setter private String description; | |||
@ECSearchable(filter=true) | |||
@Column(nullable=false, length=1024) | |||
@Getter @Setter private String url; | |||
@@ -41,31 +41,38 @@ public class BubbleApp extends IdentifiableBaseParentEntity implements AccountTe | |||
private static final String[] VALUE_FIELDS = {"url", "description", "template", "enabled"}; | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(length=UUID_MAXLEN, nullable=false, updatable=false) | |||
@Getter @Setter private String account; | |||
@ECSearchable(filter=true) | |||
@HasValue(message="err.name.required") | |||
@ECIndex @Column(nullable=false, updatable=false, length=200) | |||
@Getter @Setter private String name; | |||
@ECSearchable(filter=true) | |||
@HasValue(message="err.url.required") | |||
@Size(max=1024, message="err.url.length") | |||
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(1024+ENC_PAD)+") NOT NULL") | |||
@Getter @Setter private String url; | |||
@ECSearchable(filter=true) | |||
@Size(max=10000, message="err.description.length") | |||
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(10000+ENC_PAD)+")") | |||
@Getter @Setter private String description; | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean template = false; | |||
public boolean template() { return template != null && template; } | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean enabled = true; | |||
public boolean enabled () { return enabled == null || enabled; } | |||
@ECSearchable | |||
@ECIndex @Getter @Setter private Boolean needsUpdate = false; | |||
public BubbleApp(Account account, BubbleApp app) { | |||
@@ -50,28 +50,35 @@ public class RuleDriver extends IdentifiableBase implements AccountTemplate { | |||
return this; | |||
} | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable(filter=true) | |||
@HasValue(message="err.name.required") | |||
@ECIndex @Column(length=200, nullable=false, updatable=false) | |||
@Getter @Setter private String name; | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean template = false; | |||
public boolean template() { return template != null && template; } | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean enabled = true; | |||
public boolean enabled () { return enabled == null || enabled; } | |||
@ECSearchable(filter=true) | |||
@Column(length=200) | |||
@Getter @Setter private String author; | |||
@ECSearchable(filter=true) | |||
@Column(length=1024) | |||
@Getter @Setter private String url; | |||
@ECSearchable | |||
@Column(nullable=false, updatable=false, length=1000) | |||
@Getter @Setter private String driverClass; | |||
@@ -84,6 +91,8 @@ public class RuleDriver extends IdentifiableBase implements AccountTemplate { | |||
@Transient @JsonIgnore @Getter(lazy=true) private final AppRuleDriver driver = instantiate(this.driverClass); | |||
@Embedded @Getter @Setter private SemanticVersion version; | |||
@ECSearchable | |||
@ECIndex @Getter @Setter private Boolean needsUpdate = false; | |||
@Transient @Getter @Setter private AppRuleDriverDescriptor descriptor; | |||
@@ -26,35 +26,44 @@ import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.*; | |||
@Entity @NoArgsConstructor @Accessors(chain=true) | |||
public class AccountPayment extends IdentifiableBase implements HasAccountNoName { | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable | |||
@ECForeignKey(entity=AccountPaymentMethod.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String paymentMethod; | |||
@ECSearchable | |||
@ECForeignKey(entity=BubblePlan.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String plan; | |||
@ECSearchable | |||
@ECForeignKey(entity=AccountPlan.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String accountPlan; | |||
@ECSearchable | |||
@ECForeignKey(entity=Bill.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String bill; | |||
@ECSearchable | |||
@Enumerated(EnumType.STRING) @Column(nullable=false, updatable=false, length=20) | |||
@Getter @Setter private AccountPaymentType type; | |||
@ECSearchable | |||
@Enumerated(EnumType.STRING) @Column(nullable=false, length=20) | |||
@Getter @Setter private AccountPaymentStatus status; | |||
@ECSearchable | |||
@Type(type=ENCRYPTED_STRING) @Column(updatable=false, columnDefinition="varchar("+(200+ENC_PAD)+")") | |||
@Getter @Setter private String violation; | |||
@ECSearchable | |||
@Type(type=ENCRYPTED_STRING) @Column(updatable=false, columnDefinition="varchar("+(10000+ENC_PAD)+")") | |||
@JsonIgnore @Getter @Setter private String exception; | |||
@@ -68,12 +77,15 @@ public class AccountPayment extends IdentifiableBase implements HasAccountNoName | |||
return this; | |||
} | |||
@ECSearchable | |||
@Type(type=ENCRYPTED_LONG) @Column(updatable=false, columnDefinition="varchar("+(ENC_LONG)+") NOT NULL") | |||
@Getter @Setter private Long amount = 0L; | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false, updatable=false, length=10) | |||
@Getter @Setter private String currency; | |||
@ECSearchable(filter=true) | |||
@Type(type=ENCRYPTED_STRING) @Column(updatable=false, columnDefinition="varchar("+(100000+ENC_PAD)+") NOT NULL") | |||
@Getter @Setter private String info; | |||
@@ -16,10 +16,7 @@ import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.wizard.filters.Scrubbable; | |||
import org.cobbzilla.wizard.filters.ScrubbableField; | |||
import org.cobbzilla.wizard.model.IdentifiableBase; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECForeignKey; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECIndex; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECIndexes; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECType; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.*; | |||
import org.cobbzilla.wizard.validation.ValidationResult; | |||
import org.hibernate.annotations.Type; | |||
@@ -53,15 +50,18 @@ public class AccountPaymentMethod extends IdentifiableBase implements HasAccount | |||
@Override public void beforeCreate() { if (!hasUuid()) initUuid(); } | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(length=UUID_MAXLEN, nullable=false, updatable=false) | |||
@Getter @Setter private String account; | |||
@ECSearchable | |||
@ECForeignKey(entity=CloudService.class) | |||
@Column(length=UUID_MAXLEN, nullable=false, updatable=false) | |||
@Getter @Setter private String cloud; | |||
public boolean hasCloud() { return cloud != null; } | |||
@ECSearchable | |||
@Enumerated(EnumType.STRING) @Column(nullable=false, updatable=false, length=20) | |||
@Getter @Setter private PaymentMethodType paymentMethodType; | |||
public boolean hasPaymentMethodType() { return paymentMethodType != null; } | |||
@@ -71,9 +71,11 @@ public class AccountPaymentMethod extends IdentifiableBase implements HasAccount | |||
public boolean hasPaymentInfo () { return paymentInfo != null; } | |||
public static final String DEFAULT_MASKED_PAYMENT_INFO = "XXXX-".repeat(3)+"XXXX"; | |||
@ECSearchable | |||
@Type(type=ENCRYPTED_STRING) @Column(updatable=false, columnDefinition="varchar("+(100+ENC_PAD)+") NOT NULL") | |||
@Getter @Setter private String maskedPaymentInfo = DEFAULT_MASKED_PAYMENT_INFO; | |||
@ECSearchable | |||
@Column(nullable=false) | |||
@Getter @Setter private Boolean deleted = false; | |||
public boolean deleted() { return deleted != null && deleted; } | |||
@@ -10,6 +10,7 @@ import lombok.NoArgsConstructor; | |||
import lombok.Setter; | |||
import lombok.experimental.Accessors; | |||
import org.cobbzilla.wizard.model.IdentifiableBase; | |||
import org.cobbzilla.wizard.model.entityconfig.EntityFieldType; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.*; | |||
import javax.persistence.Column; | |||
@@ -43,51 +44,63 @@ public class AccountPlan extends IdentifiableBase implements HasAccount { | |||
@Override public void beforeCreate() { if (!hasUuid()) initUuid(); } | |||
// mirrors network name | |||
@ECSearchable(filter=true) | |||
@Size(max=100, message="err.name.length") | |||
@Column(length=100, nullable=false) | |||
@Getter @Setter private String name; | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable | |||
@ECForeignKey(entity=BubblePlan.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String plan; | |||
@ECSearchable | |||
@ECForeignKey(entity=AccountPaymentMethod.class) | |||
@Column(updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String paymentMethod; | |||
@ECSearchable | |||
@ECForeignKey(entity=BubbleDomain.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String domain; | |||
@ECSearchable | |||
@ECForeignKey(entity=BubbleNetwork.class, index=false) @ECIndex(unique=true) | |||
@Column(length=UUID_MAXLEN) | |||
@Getter @Setter private String network; | |||
@ECSearchable | |||
@Column(nullable=false) | |||
@Getter @Setter private Boolean enabled = false; | |||
public boolean enabled() { return enabled != null && enabled; } | |||
public boolean disabled() { return !enabled(); } | |||
@ECSearchable(type=EntityFieldType.epoch_time) | |||
@Column(nullable=false) | |||
@ECIndex @Getter @Setter private Long nextBill; | |||
@ECSearchable | |||
@Column(nullable=false, length=20) | |||
@Getter @Setter private String nextBillDate; | |||
public AccountPlan setNextBillDate() { return setNextBillDate(BILL_START_END_FORMAT.print(getNextBill())); } | |||
@ECSearchable | |||
@ECIndex @Getter @Setter private Long deleted; | |||
public boolean deleted() { return deleted != null; } | |||
public boolean notDeleted() { return !deleted(); } | |||
@ECSearchable | |||
@Column(nullable=false) | |||
@ECIndex @Getter @Setter private Boolean closed = false; | |||
public boolean closed() { return closed != null && closed; } | |||
public boolean notClosed() { return !closed(); } | |||
@ECSearchable | |||
@ECIndex(unique=true) @Column(length=UUID_MAXLEN) | |||
@Getter @Setter private String deletedNetwork; | |||
public boolean hasDeletedNetwork() { return deletedNetwork != null; } | |||
@@ -24,48 +24,60 @@ import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.*; | |||
}) | |||
public class Bill extends IdentifiableBase implements HasAccountNoName { | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable | |||
@ECForeignKey(entity=BubblePlan.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String plan; | |||
@ECSearchable | |||
@ECForeignKey(entity=AccountPlan.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String accountPlan; | |||
@ECSearchable | |||
@ECIndex @Enumerated(EnumType.STRING) | |||
@Column(nullable=false, length=20) | |||
@Getter @Setter private BillStatus status = BillStatus.unpaid; | |||
public boolean paid() { return status == BillStatus.paid; } | |||
public boolean unpaid() { return !paid(); } | |||
@ECSearchable | |||
@ECIndex @Enumerated(EnumType.STRING) | |||
@Column(nullable=false, updatable=false, length=20) | |||
@Getter @Setter private BillItemType type; | |||
@ECSearchable | |||
@Column(nullable=false, updatable=false, length=20) | |||
@ECIndex @Getter @Setter private String periodLabel; | |||
@ECSearchable | |||
@Column(nullable=false, updatable=false, length=20) | |||
@Getter @Setter private String periodStart; | |||
@ECSearchable | |||
@Column(nullable=false, updatable=false, length=20) | |||
@Getter @Setter private String periodEnd; | |||
public int daysInPeriod () { return BillPeriod.daysInPeriod(periodStart, periodEnd); } | |||
@ECSearchable | |||
@Type(type=ENCRYPTED_LONG) @Column(updatable=false, columnDefinition="varchar("+(ENC_LONG)+") NOT NULL") | |||
@Getter @Setter private Long quantity = 0L; | |||
@ECSearchable | |||
@Type(type=ENCRYPTED_LONG) @Column(updatable=false, columnDefinition="varchar("+(ENC_LONG)+") NOT NULL") | |||
@Getter @Setter private Long price = 0L; | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false, updatable=false, length=10) | |||
@Getter @Setter private String currency; | |||
@ECSearchable | |||
@Type(type=ENCRYPTED_LONG) @Column(columnDefinition="varchar("+(ENC_LONG)+")") | |||
@Getter @Setter private Long refundedAmount = 0L; | |||
public boolean hasRefundedAmount () { return refundedAmount != null && refundedAmount > 0L; } | |||
@@ -44,14 +44,17 @@ public class BubblePlan extends IdentifiableBase implements HasAccount { | |||
public BubblePlan (BubblePlan other) { copy(this, other, CREATE_FIELDS); } | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable(filter=true) | |||
@HasValue(message="err.name.required") | |||
@ECIndex @Column(nullable=false, updatable=false, length=200) | |||
@Getter @Setter private String name; | |||
@ECSearchable(filter=true) | |||
@HasValue(message="err.chargeName.required") | |||
@Size(message="err.chargeName.length") | |||
@Column(nullable=false, updatable=false, length=12) | |||
@@ -68,37 +71,48 @@ public class BubblePlan extends IdentifiableBase implements HasAccount { | |||
} | |||
} | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean enabled = true; | |||
public boolean enabled () { return enabled == null || enabled; } | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Long price; | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false, length=10) | |||
@Getter @Setter private String currency = "USD"; | |||
@ECSearchable | |||
@Enumerated(EnumType.STRING) @Column(nullable=false, updatable=false, length=20) | |||
@Getter @Setter private BillPeriod period = BillPeriod.monthly; | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false, updatable=false, length=20) | |||
@Enumerated(EnumType.STRING) @Getter @Setter private ComputeNodeSizeType computeSizeType; | |||
@ECSearchable | |||
@Column(nullable=false, updatable=false) | |||
@Getter @Setter private Integer nodesIncluded; | |||
@ECSearchable | |||
@Column(nullable=false, updatable=false) | |||
@Getter @Setter private Integer additionalPerNodePrice; | |||
@ECSearchable | |||
@Column(nullable=false, updatable=false) | |||
@Getter @Setter private Integer storageGbIncluded; | |||
@ECSearchable | |||
@Column(nullable=false, updatable=false) | |||
@Getter @Setter private Integer additionalStoragePerGbPrice; | |||
@ECSearchable | |||
@Column(nullable=false, updatable=false) | |||
@Getter @Setter private Integer bandwidthGbIncluded; | |||
@ECSearchable | |||
@Column(nullable=false, updatable=false) | |||
@Getter @Setter private Integer additionalBandwidthPerGbPrice; | |||
@@ -43,10 +43,12 @@ public class AnsibleRole extends IdentifiableBase implements AccountTemplate, Ha | |||
public AnsibleRole(AnsibleRole role) { copy(this, role, COPY_FIELDS); } | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable(filter=true) | |||
@HasValue(message="err.name.required") | |||
@Pattern(regexp=ROLENAME_PATTERN, message="err.name.invalid") | |||
@ECIndex @Column(nullable=false, updatable=false, length=200) | |||
@@ -74,9 +76,11 @@ public class AnsibleRole extends IdentifiableBase implements AccountTemplate, Ha | |||
.orElse(null); | |||
} | |||
@ECSearchable(filter=true) | |||
@Size(max=10000, message="err.description.length") | |||
@Getter @Setter private String description; | |||
@ECSearchable | |||
@Enumerated(EnumType.STRING) | |||
@ECIndex @Column(nullable=false, length=20) | |||
@Getter @Setter private AnsibleInstallType install = AnsibleInstallType.standard; | |||
@@ -84,13 +88,16 @@ public class AnsibleRole extends IdentifiableBase implements AccountTemplate, Ha | |||
return install.shouldInstall(installType); | |||
} | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Integer priority; | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean template = false; | |||
public boolean template() { return template != null && template; } | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false) | |||
@Getter @Setter private Boolean enabled = true; | |||
public boolean enabled () { return enabled == null || enabled; } | |||
@@ -33,18 +33,22 @@ public class BubbleBackup extends IdentifiableBase implements HasAccount { | |||
if (getUuid() == null) initUuid(); | |||
} | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable | |||
@ECForeignKey(entity=BubbleNetwork.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String network; | |||
@ECSearchable(filter=true) | |||
@Size(max=2000, message="err.path.length") | |||
@Type(type=ENCRYPTED_STRING) @Column(updatable=false, columnDefinition="varchar("+(2000+ENC_PAD)+") NOT NULL") | |||
@Getter @Setter private String path; | |||
@ECSearchable(filter=true) | |||
@Pattern(regexp="[A-Za-z0-9][-A-Za-z0-9\\._]{2,}", message="err.label.invalid") | |||
@Size(max=300, message="err.label.length") | |||
@Type(type=ENCRYPTED_STRING) @Column(updatable=false, columnDefinition="varchar("+(300+ENC_PAD)+")") | |||
@@ -53,12 +57,14 @@ public class BubbleBackup extends IdentifiableBase implements HasAccount { | |||
@Override @JsonIgnore @Transient public String getName() { return hasLabel() ? getLabel() : getPath(); } | |||
@ECSearchable | |||
@Enumerated(EnumType.STRING) | |||
@ECIndex @Column(nullable=false, length=40) | |||
@Getter @Setter private BackupStatus status; | |||
public boolean success () { return status == BackupStatus.backup_completed; } | |||
@Column(length=ERROR_MAXLEN) | |||
@ECSearchable(filter=true) | |||
@Type(type=ENCRYPTED_STRING) @Column(length=ERROR_MAXLEN) | |||
@Getter @Setter private String error; | |||
public boolean hasError () { return !empty(error); } | |||
@@ -53,6 +53,7 @@ public class BubbleDomain extends IdentifiableBase implements AccountTemplate { | |||
@Override public Identifiable update(Identifiable other) { copy(this, other, UPDATE_FIELDS); return this; } | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@@ -59,6 +59,7 @@ public class BubbleFootprint extends IdentifiableBase implements AccountTemplate | |||
@Override public Identifiable update(Identifiable other) { copy(this, other, UPDATE_FIELDS); return this; } | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@@ -33,7 +33,7 @@ import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENCRYPTED_STRING; | |||
import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENC_PAD; | |||
@ECType(root=true) | |||
@ECTypeURIs(listFields={"name", "domain", "description", "account", "enabled"}) | |||
@ECTypeURIs(baseURI=EP_NETWORKS, listFields={"name", "domain", "description", "account", "enabled"}) | |||
@ECTypeFields(list={"name", "domain", "description", "account", "enabled"}) | |||
@ECTypeChildren(uriPrefix=EP_NETWORKS+"/{BubbleNetwork.name}", value={ | |||
@ECTypeChild(type=BubbleNode.class, backref="network") | |||
@@ -57,6 +57,7 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu | |||
@Transient @JsonIgnore public String getNetwork () { return getUuid(); } | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@@ -38,6 +38,7 @@ import static org.cobbzilla.util.network.NetworkUtil.isLocalIpv4; | |||
import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | |||
import static org.cobbzilla.util.string.ValidationRegexes.IP4_MAXLEN; | |||
import static org.cobbzilla.util.string.ValidationRegexes.IP6_MAXLEN; | |||
import static org.cobbzilla.wizard.model.entityconfig.annotations.ECForeignKeySearchDepth.shallow; | |||
@ECType(root=true) | |||
@ECTypeURIs(baseURI=EP_NODES, listFields={"name", "ip4"}) | |||
@@ -86,11 +87,12 @@ public class BubbleNode extends IdentifiableBase implements HasNetwork, HasBubbl | |||
@JsonIgnore @Transient @Override public String getName() { return getFqdn(); } | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable | |||
@ECSearchable(fkDepth=shallow) | |||
@ECForeignKey(entity=BubbleDomain.class) | |||
@HasValue(message="err.network.required") | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@@ -99,7 +101,7 @@ public class BubbleNode extends IdentifiableBase implements HasNetwork, HasBubbl | |||
return getDomain() != null && n.getDomain() != null && getDomain().equals(n.getDomain()); | |||
} | |||
@ECSearchable | |||
@ECSearchable(fkDepth=shallow) | |||
@ECForeignKey(entity=BubbleNetwork.class) | |||
@HasValue(message="err.network.required") | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@@ -1,7 +1,6 @@ | |||
package bubble.model.cloud; | |||
import bubble.model.account.Account; | |||
import bubble.model.account.HasAccount; | |||
import bubble.model.account.HasAccountNoName; | |||
import com.fasterxml.jackson.annotation.JsonIgnore; | |||
import lombok.Getter; | |||
@@ -12,8 +11,10 @@ import lombok.experimental.Accessors; | |||
import org.cobbzilla.util.security.RsaKeyPair; | |||
import org.cobbzilla.util.security.RsaMessage; | |||
import org.cobbzilla.wizard.model.IdentifiableBase; | |||
import org.cobbzilla.wizard.model.entityconfig.EntityFieldType; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECForeignKey; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECIndex; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECSearchable; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECType; | |||
import org.hibernate.annotations.Type; | |||
@@ -81,14 +82,17 @@ public class BubbleNodeKey extends IdentifiableBase implements HasAccountNoName | |||
return keys.stream().allMatch(k -> k.expiresInLessThan(TOKEN_GENERATION_LIMIT)); | |||
} | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable | |||
@ECForeignKey(entity=BubbleNode.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String node; | |||
@ECSearchable(filter=true) | |||
@Column(length=10000, updatable=false, nullable=false) | |||
@Getter private String publicKey; | |||
public BubbleNodeKey setPublicKey (String k) { | |||
@@ -114,10 +118,12 @@ public class BubbleNodeKey extends IdentifiableBase implements HasAccountNoName | |||
@Type(type=ENCRYPTED_STRING) @ECIndex(unique=true) @Column(updatable=false, columnDefinition="varchar("+(100+ENC_PAD)+")") | |||
@Getter @Setter private String privateKeyHash; | |||
@ECSearchable(filter=true) | |||
@Size(max=100, message="err.remoteHost.length") | |||
@Type(type=ENCRYPTED_STRING) @Column(updatable=false, columnDefinition="varchar("+(100+ENC_PAD)+") NOT NULL") | |||
@Getter @Setter private String remoteHost; | |||
@ECSearchable(type=EntityFieldType.epoch_time) | |||
@ECIndex @Column(nullable=false, updatable=false) | |||
@Getter @Setter private Long expiration = defaultExpiration(); | |||
@@ -72,6 +72,7 @@ public class CloudService extends IdentifiableBaseParentEntity implements Accoun | |||
@Override public Identifiable update(Identifiable thing) { copy(this, thing, UPDATE_FIELDS); return this; } | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@@ -13,6 +13,7 @@ import org.cobbzilla.util.daemon.ZillaRuntime; | |||
import org.cobbzilla.wizard.model.IdentifiableBase; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECForeignKey; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECIndex; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECSearchable; | |||
import javax.persistence.*; | |||
@@ -23,31 +24,38 @@ import static org.cobbzilla.util.json.JsonUtil.json; | |||
public class NotificationBase extends IdentifiableBase implements HasAccountNoName { | |||
// synchronous requests may include this | |||
@ECSearchable | |||
@Column(updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String notificationId; | |||
public boolean hasId () { return notificationId != null; } | |||
@ECSearchable | |||
@ECForeignKey(entity=Account.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String account; | |||
@ECSearchable(filter=true) | |||
@ECIndex @Column(nullable=false, updatable=false, length=50) | |||
@Enumerated(EnumType.STRING) @Getter @Setter private NotificationType type; | |||
@Getter @Setter private boolean resolveNodes = false; | |||
@ECSearchable | |||
@ECForeignKey(entity=BubbleNode.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String fromNode; | |||
public boolean hasFromNode () { return fromNode != null; } | |||
@ECSearchable | |||
@ECForeignKey(entity=BubbleNode.class) | |||
@Column(nullable=false, updatable=false, length=UUID_MAXLEN) | |||
@Getter @Setter private String toNode; | |||
@ECSearchable(filter=true) | |||
@Column(nullable=false, updatable=false, length=1024) | |||
@Getter @Setter private String uri; | |||
@ECSearchable(filter=true) | |||
@Column(updatable=false, length=100_000) | |||
@JsonIgnore @Getter @Setter private String payloadJson; | |||
public boolean hasPayload () { return payloadJson != null; } | |||
@@ -66,6 +74,7 @@ public class NotificationBase extends IdentifiableBase implements HasAccountNoNa | |||
@Transient public NotificationReceipt getReceipt () { return receiptJson == null ? null : json(receiptJson, NotificationReceipt.class); } | |||
public <T extends NotificationBase> T setReceipt (NotificationReceipt receipt) { return (T) setReceiptJson(receipt == null ? null : json(receiptJson)); } | |||
@ECSearchable(filter=true) | |||
@Column(length=ERROR_MAXLEN) | |||
@JsonIgnore @Getter @Setter private String error; | |||
@@ -4,10 +4,7 @@ import lombok.Getter; | |||
import lombok.NoArgsConstructor; | |||
import lombok.Setter; | |||
import lombok.experimental.Accessors; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECIndex; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECType; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECTypeFields; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECTypeURIs; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.*; | |||
import javax.persistence.Column; | |||
import javax.persistence.Entity; | |||
@@ -25,6 +22,7 @@ public class ReceivedNotification extends NotificationBase { | |||
public ReceivedNotification(SentNotification notification) { copy(this, notification); setUuid(null); } | |||
@ECSearchable(filter=true) | |||
@ECIndex @Column(nullable=false, length=20) | |||
@Enumerated(EnumType.STRING) @Getter @Setter private NotificationProcessingStatus processingStatus = NotificationProcessingStatus.received; | |||
@@ -4,10 +4,7 @@ import lombok.Getter; | |||
import lombok.NoArgsConstructor; | |||
import lombok.Setter; | |||
import lombok.experimental.Accessors; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECIndex; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECType; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECTypeFields; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.ECTypeURIs; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.*; | |||
import javax.persistence.Column; | |||
import javax.persistence.Entity; | |||
@@ -22,6 +19,7 @@ import static bubble.ApiConstants.EP_SENT_NOTIFICATIONS; | |||
@Entity @NoArgsConstructor @Accessors(chain=true) | |||
public class SentNotification extends NotificationBase { | |||
@ECSearchable | |||
@ECIndex @Column(nullable=false, length=20) | |||
@Enumerated(EnumType.STRING) @Getter @Setter private NotificationSendStatus status = NotificationSendStatus.created; | |||
@@ -35,7 +35,7 @@ public class EntityConfigsResource extends AbstractEntityConfigsResource { | |||
@Autowired private AccountDAO accountDAO; | |||
@Getter(AccessLevel.PROTECTED) @Autowired private BubbleConfiguration configuration; | |||
private AtomicBoolean allowPublic = new AtomicBoolean(false); | |||
@Getter private AtomicBoolean allowPublic = new AtomicBoolean(false); | |||
@POST @Path("/set/{param}") | |||
public Response setConfig (@Context ContainerRequest ctx, | |||
@@ -0,0 +1,74 @@ | |||
package bubble.resources; | |||
import bubble.dao.account.AccountDAO; | |||
import bubble.dao.account.AccountOwnedEntityDAO; | |||
import bubble.model.account.Account; | |||
import bubble.server.BubbleConfiguration; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.wizard.dao.DAO; | |||
import org.cobbzilla.wizard.model.Identifiable; | |||
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; | |||
import javax.ws.rs.*; | |||
import javax.ws.rs.core.Context; | |||
import javax.ws.rs.core.Response; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import static bubble.ApiConstants.ID_ENDPOINT; | |||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | |||
@Consumes(APPLICATION_JSON) | |||
@Produces(APPLICATION_JSON) | |||
@Path(ID_ENDPOINT) | |||
@Service @Slf4j | |||
public class IdentityResource { | |||
@Autowired private BubbleConfiguration configuration; | |||
@GET | |||
public Response identifyNothing(@Context Request req, | |||
@Context ContainerRequest ctx) { return ok_empty(); } | |||
@GET @Path("/{id}") | |||
public Response identify(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final Account caller = userPrincipal(ctx); | |||
final Map<String, Identifiable> entities = new HashMap<>(); | |||
for (Class<? extends Identifiable> type : configuration.getEntityClasses()) { | |||
final DAO dao = configuration.getDaoForEntityClass(type); | |||
final Identifiable found; | |||
if (dao instanceof AccountOwnedEntityDAO) { | |||
// find things we own with the given id | |||
found = ((AccountOwnedEntityDAO) dao).findByAccountAndId(caller.getUuid(), id); | |||
} else if (dao instanceof AccountDAO) { | |||
if (caller.admin()) { | |||
// only admin can find any user | |||
found = ((AccountDAO) dao).findById(id); | |||
} else if (id.equals(caller.getUuid()) || id.equals(caller.getName())) { | |||
// other callers can find themselves | |||
found = caller; | |||
} else { | |||
found = null; | |||
} | |||
} else if (caller.admin()) { | |||
// admins can find anything anywhere, regardless of who owns it | |||
found = dao.findByUuid(id); | |||
} else { | |||
// everything else is not found | |||
found = null; | |||
} | |||
if (found != null) entities.put(type.getName(), found); | |||
} | |||
return ok(entities); | |||
} | |||
} |
@@ -0,0 +1,121 @@ | |||
package bubble.resources; | |||
import bubble.model.account.Account; | |||
import bubble.server.BubbleConfiguration; | |||
import bubble.service.cloud.GeoService; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.util.collection.ExpirationMap; | |||
import org.cobbzilla.wizard.dao.AbstractDAO; | |||
import org.cobbzilla.wizard.dao.DAO; | |||
import org.cobbzilla.wizard.dao.SearchResults; | |||
import org.cobbzilla.wizard.model.search.SearchQuery; | |||
import org.cobbzilla.wizard.model.search.SqlViewField; | |||
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; | |||
import javax.ws.rs.*; | |||
import javax.ws.rs.core.Context; | |||
import javax.ws.rs.core.Response; | |||
import java.util.Map; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
import static bubble.ApiConstants.*; | |||
import static java.util.concurrent.TimeUnit.MINUTES; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.hashOf; | |||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | |||
@Consumes(APPLICATION_JSON) | |||
@Produces(APPLICATION_JSON) | |||
@Path(SEARCH_ENDPOINT) | |||
@Service @Slf4j | |||
public class SearchResource { | |||
@Autowired private BubbleConfiguration configuration; | |||
@Autowired private GeoService geoService; | |||
public static final String Q_FILTER = "query"; | |||
public static final String Q_META = "meta"; | |||
public static final String Q_PAGE = "page"; | |||
public static final String Q_SIZE = "size"; | |||
private Map<String, DAO> daoCache = new ConcurrentHashMap<>(); | |||
@GET @Path("/{type}") | |||
public Response search(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("type") String type, | |||
@QueryParam(Q_META) Boolean meta, | |||
@QueryParam(Q_FILTER) String filter, | |||
@QueryParam(Q_PAGE) Integer page, | |||
@QueryParam(Q_SIZE) Integer size) { | |||
return search(req, ctx, type, meta, filter, page, size, null); | |||
} | |||
private Map<String, Object> _searchCache = new ExpirationMap<>(MINUTES.toMillis(10), MINUTES.toMillis(15)); | |||
@POST @Path("/{type}") | |||
public Response search(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("type") String type, | |||
@QueryParam(Q_META) Boolean meta, | |||
@QueryParam(Q_FILTER) String filter, | |||
@QueryParam(Q_PAGE) Integer page, | |||
@QueryParam(Q_SIZE) Integer size, | |||
SearchQuery searchQuery) { | |||
final Account caller = userPrincipal(ctx); | |||
final String cacheKey = hashOf(caller.getUuid(), type, meta, filter, page, size, searchQuery); | |||
return ok(_searchCache.computeIfAbsent(cacheKey, searchKey -> { | |||
final DAO dao = daoCache.computeIfAbsent(type, k -> getDao(type)); | |||
if (meta != null) return ((AbstractDAO) dao).getSearchFields(); | |||
final SearchQuery q = searchQuery != null ? searchQuery : new SearchQuery(); | |||
if (!q.hasLocale()) { | |||
try { | |||
q.setLocale(geoService.getFirstLocale(caller, getRemoteHost(req), normalizeLangHeader(req))); | |||
} catch (Exception e) { | |||
log.warn("search: error setting locale, using default: "+configuration.getDefaultLocale()+": "+e); | |||
q.setLocale(configuration.getDefaultLocale()); | |||
} | |||
} | |||
q.setPageNumber(page != null ? page : 1); | |||
q.setPageSize(size != null ? Integer.min(size, MAX_SEARCH_PAGE) : Integer.min(q.getPageSize(), MAX_SEARCH_PAGE)); | |||
if (filter != null) q.setFilter(filter); | |||
if (!caller.admin()) { | |||
final Class entityClass = dao.getEntityClass(); | |||
if (entityClass.equals(Account.class)) { | |||
// non-admins can only look up themselves | |||
q.setBound("uuid", caller.getUuid()); | |||
} else { | |||
final SqlViewField accountField = ((AbstractDAO) dao).findSearchField(entityClass, "account"); | |||
if (accountField != null) { | |||
q.setBound("account", caller.getUuid()); | |||
} else { | |||
// no results, non-admin cannot search for things that do not have an account | |||
return new SearchResults<>().setError("cannot search "+ entityClass.getName()); | |||
} | |||
} | |||
} | |||
final SearchResults results = dao.search(q); | |||
if (results.hasNextPage(q)) { | |||
results.setNextPage(req.getRequestURI()+"?"+Q_PAGE+"="+(q.getPageNumber()+1)+"&"+Q_SIZE+"="+q.getPageSize()); | |||
} | |||
return results; | |||
})); | |||
} | |||
public DAO getDao(String type) { | |||
for (Class c : configuration.getEntityClasses()) { | |||
if (c.getSimpleName().equalsIgnoreCase(type)) { | |||
return configuration.getDaoForEntityClass(c); | |||
} | |||
} | |||
throw notFoundEx(type); | |||
} | |||
} |
@@ -2,7 +2,6 @@ package bubble.resources.account; | |||
import bubble.dao.SessionDAO; | |||
import bubble.dao.account.AccountDAO; | |||
import bubble.dao.account.AccountOwnedEntityDAO; | |||
import bubble.dao.account.AccountPolicyDAO; | |||
import bubble.model.account.Account; | |||
import bubble.model.account.AccountPolicy; | |||
@@ -20,7 +19,6 @@ import bubble.resources.notify.SentNotificationsResource; | |||
import bubble.server.BubbleConfiguration; | |||
import bubble.service.account.StandardAccountMessageService; | |||
import bubble.service.account.download.AccountDownloadService; | |||
import bubble.service.cloud.GeoService; | |||
import bubble.service.cloud.StandardNetworkService; | |||
import com.fasterxml.jackson.databind.JsonNode; | |||
import lombok.Cleanup; | |||
@@ -31,10 +29,7 @@ import org.cobbzilla.wizard.client.script.ApiRunner; | |||
import org.cobbzilla.wizard.client.script.ApiRunnerListener; | |||
import org.cobbzilla.wizard.client.script.ApiRunnerListenerStreamLogger; | |||
import org.cobbzilla.wizard.client.script.ApiScript; | |||
import org.cobbzilla.wizard.dao.DAO; | |||
import org.cobbzilla.wizard.model.HashedPassword; | |||
import org.cobbzilla.wizard.model.Identifiable; | |||
import org.cobbzilla.wizard.model.search.SearchQuery; | |||
import org.glassfish.grizzly.http.server.Request; | |||
import org.glassfish.jersey.server.ContainerRequest; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
@@ -44,9 +39,6 @@ import javax.ws.rs.*; | |||
import javax.ws.rs.core.Context; | |||
import javax.ws.rs.core.Response; | |||
import java.io.StringWriter; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
import static bubble.ApiConstants.*; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.errorString; | |||
@@ -266,72 +258,4 @@ public class MeResource { | |||
return ok(networkService.listLaunchStatuses(caller.getUuid())); | |||
} | |||
@GET @Path(EP_ID) | |||
public Response identifyNothing(@Context Request req, | |||
@Context ContainerRequest ctx) { return ok_empty(); } | |||
@GET @Path(EP_ID+"/{id}") | |||
public Response identify(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final Account caller = userPrincipal(ctx); | |||
final Map<String, Identifiable> entities = new HashMap<>(); | |||
for (Class<? extends Identifiable> type : configuration.getEntityClasses()) { | |||
final DAO dao = configuration.getDaoForEntityClass(type); | |||
final Identifiable found; | |||
if (dao instanceof AccountOwnedEntityDAO) { | |||
// find things we own with the given id | |||
found = ((AccountOwnedEntityDAO) dao).findByAccountAndId(caller.getUuid(), id); | |||
} else if (dao instanceof AccountDAO) { | |||
if (caller.admin()) { | |||
// only admin can find any user | |||
found = ((AccountDAO) dao).findById(id); | |||
} else if (id.equals(caller.getUuid()) || id.equals(caller.getName())) { | |||
// other callers can find themselves | |||
found = caller; | |||
} else { | |||
found = null; | |||
} | |||
} else if (caller.admin()) { | |||
// admins can find anything anywhere, regardless of who owns it | |||
found = dao.findByUuid(id); | |||
} else { | |||
// everything else is not found | |||
found = null; | |||
} | |||
if (found != null) entities.put(type.getName(), found); | |||
} | |||
return ok(entities); | |||
} | |||
@Autowired private GeoService geoService; | |||
private Map<String, DAO> daoCache = new ConcurrentHashMap<>(); | |||
@POST @Path(EP_SEARCH+"/{type}") | |||
public Response search(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("type") String type, | |||
SearchQuery searchQuery) { | |||
final Account caller = userPrincipal(ctx); | |||
final DAO dao = daoCache.computeIfAbsent(type, k -> getDao(type)); | |||
if (searchQuery == null) searchQuery = new SearchQuery(); | |||
if (!searchQuery.hasLocale()) { | |||
searchQuery.setLocale(geoService.getFirstLocale(caller, getRemoteHost(req), normalizeLangHeader(req))); | |||
} | |||
return ok(dao.search(searchQuery)); | |||
} | |||
public DAO getDao(String type) { | |||
for (Class c : configuration.getEntityClasses()) { | |||
if (c.getSimpleName().equalsIgnoreCase(type)) { | |||
return configuration.getDaoForEntityClass(c); | |||
} | |||
} | |||
throw notFoundEx(type); | |||
} | |||
} |
@@ -1,7 +1,10 @@ | |||
package bubble.test.dev; | |||
import bubble.resources.EntityConfigsResource; | |||
import bubble.server.BubbleConfiguration; | |||
import bubble.test.ActivatedBubbleModelTestBase; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.wizard.server.RestServer; | |||
import org.junit.Test; | |||
import static java.util.concurrent.TimeUnit.DAYS; | |||
@@ -20,6 +23,11 @@ public class DevServerTest extends ActivatedBubbleModelTestBase { | |||
@Override protected boolean allowPreExistingDatabase() { return true; } | |||
@Override public boolean doTruncateDb() { return false; } | |||
@Override public void onStart(RestServer<BubbleConfiguration> server) { | |||
getConfiguration().getBean(EntityConfigsResource.class).getAllowPublic().set(true); | |||
super.onStart(server); | |||
} | |||
@Test public void runDevServer () throws Exception { | |||
log.info("runDevServer: Bubble API server started and model initialized. You may now begin testing."); | |||
sleep(DAYS.toMillis(30), "running dev server"); | |||
@@ -1 +1 @@ | |||
Subproject commit 708996280e324bb3e5c819743a477ea988312d86 | |||
Subproject commit f804680fd96b25582d9a269cfcd82f173c7da4d4 |
@@ -1 +1 @@ | |||
Subproject commit 65c0a83a885608d917c1d1a4d85dd8945d35f8d4 | |||
Subproject commit 275e0f3fdd4f10f5cdb04416c0d8cbb2092c065e |