@@ -129,6 +129,7 @@ public class ApiConstants { | |||||
public static final String EP_TAGS = "/tags"; | public static final String EP_TAGS = "/tags"; | ||||
public static final String EP_NODES = "/nodes"; | public static final String EP_NODES = "/nodes"; | ||||
public static final String EP_DEVICES = "/devices"; | public static final String EP_DEVICES = "/devices"; | ||||
public static final String EP_MODEL = "/model"; | |||||
public static final String EP_VPN = "/vpn"; | public static final String EP_VPN = "/vpn"; | ||||
public static final String EP_PAYMENT_METHOD = "/paymentMethod"; | public static final String EP_PAYMENT_METHOD = "/paymentMethod"; | ||||
public static final String EP_PAYMENT_METHODS = PAYMENT_METHODS_ENDPOINT; | public static final String EP_PAYMENT_METHODS = PAYMENT_METHODS_ENDPOINT; | ||||
@@ -241,4 +242,5 @@ public class ApiConstants { | |||||
.orElseThrow((Supplier<RuntimeException>) () -> | .orElseThrow((Supplier<RuntimeException>) () -> | ||||
invalidEx("err."+e.getSimpleName()+".invalid", "Invalid "+e.getSimpleName()+": "+v, v)); | invalidEx("err."+e.getSimpleName()+".invalid", "Invalid "+e.getSimpleName()+": "+v, v)); | ||||
} | } | ||||
} | } |
@@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicBoolean; | |||||
import static bubble.ApiConstants.getRemoteHost; | import static bubble.ApiConstants.getRemoteHost; | ||||
import static bubble.model.account.AccountTemplate.copyTemplateObjects; | import static bubble.model.account.AccountTemplate.copyTemplateObjects; | ||||
import static bubble.model.account.AutoUpdatePolicy.EMPTY_AUTO_UPDATE_POLICY; | import static bubble.model.account.AutoUpdatePolicy.EMPTY_AUTO_UPDATE_POLICY; | ||||
import static bubble.server.BubbleConfiguration.getDEFAULT_LOCALE; | |||||
import static java.util.concurrent.TimeUnit.MINUTES; | import static java.util.concurrent.TimeUnit.MINUTES; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.daemon; | import static org.cobbzilla.util.daemon.ZillaRuntime.daemon; | ||||
import static org.cobbzilla.wizard.model.IdentifiableBase.CTIME_ASC; | import static org.cobbzilla.wizard.model.IdentifiableBase.CTIME_ASC; | ||||
@@ -71,6 +72,8 @@ public class AccountDAO extends AbstractCRUDDAO<Account> implements SqlViewSearc | |||||
} | } | ||||
@Override public Object preCreate(Account account) { | @Override public Object preCreate(Account account) { | ||||
if (!account.hasLocale()) account.setLocale(getDEFAULT_LOCALE()); | |||||
final ValidationResult result = account.validateName(); | final ValidationResult result = account.validateName(); | ||||
if (result.isInvalid()) throw invalidEx(result); | if (result.isInvalid()) throw invalidEx(result); | ||||
@@ -19,6 +19,8 @@ import java.io.IOException; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import static bubble.server.BubbleConfiguration.getDEFAULT_LOCALE; | |||||
@Repository @Slf4j | @Repository @Slf4j | ||||
public class BubbleNetworkDAO extends AccountOwnedEntityDAO<BubbleNetwork> { | public class BubbleNetworkDAO extends AccountOwnedEntityDAO<BubbleNetwork> { | ||||
@@ -31,6 +33,11 @@ public class BubbleNetworkDAO extends AccountOwnedEntityDAO<BubbleNetwork> { | |||||
@Autowired private SelfNodeService selfNodeService; | @Autowired private SelfNodeService selfNodeService; | ||||
@Autowired private BubbleConfiguration configuration; | @Autowired private BubbleConfiguration configuration; | ||||
@Override public Object preCreate(BubbleNetwork network) { | |||||
if (!network.hasLocale()) network.setLocale(getDEFAULT_LOCALE()); | |||||
return super.preCreate(network); | |||||
} | |||||
@Override public BubbleNetwork postUpdate(BubbleNetwork network, Object context) { | @Override public BubbleNetwork postUpdate(BubbleNetwork network, Object context) { | ||||
if (selfNodeService.getThisNetwork().getUuid().equals(network.getUuid())) { | if (selfNodeService.getThisNetwork().getUuid().equals(network.getUuid())) { | ||||
selfNodeService.refreshThisNetwork(); | selfNodeService.refreshThisNetwork(); | ||||
@@ -35,7 +35,7 @@ import java.util.List; | |||||
import java.util.regex.Pattern; | import java.util.regex.Pattern; | ||||
import static bubble.ApiConstants.ACCOUNTS_ENDPOINT; | import static bubble.ApiConstants.ACCOUNTS_ENDPOINT; | ||||
import static bubble.ApiConstants.DEFAULT_LOCALE; | |||||
import static bubble.server.BubbleConfiguration.getDEFAULT_LOCALE; | |||||
import static java.util.concurrent.TimeUnit.MILLISECONDS; | import static java.util.concurrent.TimeUnit.MILLISECONDS; | ||||
import static java.util.concurrent.TimeUnit.SECONDS; | import static java.util.concurrent.TimeUnit.SECONDS; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.*; | import static org.cobbzilla.util.daemon.ZillaRuntime.*; | ||||
@@ -116,7 +116,8 @@ public class Account extends IdentifiableBase implements TokenPrincipal, SqlView | |||||
@ECSearchable @ECField(type=EntityFieldType.locale, index=50) | @ECSearchable @ECField(type=EntityFieldType.locale, index=50) | ||||
@Size(max=20, message="err.locale.length") | @Size(max=20, message="err.locale.length") | ||||
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(20+ENC_PAD)+") NOT NULL") | @Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(20+ENC_PAD)+") NOT NULL") | ||||
@Getter @Setter private String locale = DEFAULT_LOCALE; | |||||
@Getter @Setter private String locale = getDEFAULT_LOCALE(); | |||||
public boolean hasLocale () { return !empty(locale); } | |||||
@ECSearchable @ECField(index=60) | @ECSearchable @ECField(index=60) | ||||
@Getter @Setter private Boolean admin = false; | @Getter @Setter private Boolean admin = false; | ||||
@@ -17,11 +17,15 @@ import org.cobbzilla.wizard.model.SemanticVersion; | |||||
import org.cobbzilla.wizard.model.entityconfig.annotations.*; | import org.cobbzilla.wizard.model.entityconfig.annotations.*; | ||||
import org.cobbzilla.wizard.validation.HasValue; | import org.cobbzilla.wizard.validation.HasValue; | ||||
import javax.persistence.*; | |||||
import javax.persistence.Column; | |||||
import javax.persistence.Embedded; | |||||
import javax.persistence.Entity; | |||||
import javax.persistence.Transient; | |||||
import java.util.Locale; | import java.util.Locale; | ||||
import static bubble.ApiConstants.*; | |||||
import static bubble.ApiConstants.DEFAULT_LOCALE; | |||||
import static bubble.ApiConstants.DRIVERS_ENDPOINT; | |||||
import static bubble.ApiConstants.EP_DRIVERS; | |||||
import static bubble.server.BubbleConfiguration.getDEFAULT_LOCALE; | |||||
import static org.cobbzilla.util.io.StreamUtil.stream2string; | import static org.cobbzilla.util.io.StreamUtil.stream2string; | ||||
import static org.cobbzilla.util.json.JsonUtil.json; | import static org.cobbzilla.util.json.JsonUtil.json; | ||||
import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | ||||
@@ -98,7 +102,7 @@ public class RuleDriver extends IdentifiableBase implements AccountTemplate { | |||||
@Transient @Getter @Setter private AppRuleDriverDescriptor descriptor; | @Transient @Getter @Setter private AppRuleDriverDescriptor descriptor; | ||||
public AppRuleDriverDescriptor getDescriptor(Locale locale) { | public AppRuleDriverDescriptor getDescriptor(Locale locale) { | ||||
final String localeString = locale != null ? locale.toString() : DEFAULT_LOCALE; | |||||
final String localeString = locale != null ? locale.toString() : getDEFAULT_LOCALE(); | |||||
final AppRuleDriverDescriptor localized; | final AppRuleDriverDescriptor localized; | ||||
final String prefix = driverClass.replace(".", "/"); | final String prefix = driverClass.replace(".", "/"); | ||||
try { | try { | ||||
@@ -28,8 +28,9 @@ import static bubble.ApiConstants.EP_NETWORKS; | |||||
import static bubble.ApiConstants.ROOT_NETWORK_UUID; | import static bubble.ApiConstants.ROOT_NETWORK_UUID; | ||||
import static bubble.model.cloud.BubbleDomain.DOMAIN_NAME_MAXLEN; | import static bubble.model.cloud.BubbleDomain.DOMAIN_NAME_MAXLEN; | ||||
import static bubble.model.cloud.BubbleNetworkState.created; | import static bubble.model.cloud.BubbleNetworkState.created; | ||||
import static bubble.ApiConstants.DEFAULT_LOCALE; | |||||
import static bubble.server.BubbleConfiguration.getDEFAULT_LOCALE; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.die; | import static org.cobbzilla.util.daemon.ZillaRuntime.die; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||||
import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | ||||
import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENCRYPTED_STRING; | 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.crypto.EncryptedTypes.ENC_PAD; | ||||
@@ -107,7 +108,8 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu | |||||
@ECSearchable @ECField(type=EntityFieldType.locale, index=90) | @ECSearchable @ECField(type=EntityFieldType.locale, index=90) | ||||
@Size(max=20, message="err.locale.length") | @Size(max=20, message="err.locale.length") | ||||
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(20+ENC_PAD)+") NOT NULL") | @Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(20+ENC_PAD)+") NOT NULL") | ||||
@Getter @Setter private String locale = DEFAULT_LOCALE; | |||||
@Getter @Setter private String locale = getDEFAULT_LOCALE(); | |||||
public boolean hasLocale () { return !empty(locale); } | |||||
// A unicode timezone alias from: cobbzilla-utils/src/main/resources/org/cobbzilla/util/time/unicode-timezones.xml | // A unicode timezone alias from: cobbzilla-utils/src/main/resources/org/cobbzilla/util/time/unicode-timezones.xml | ||||
// All unicode aliases are guaranteed to map to a Linux timezone and a Java timezone | // All unicode aliases are guaranteed to map to a Linux timezone and a Java timezone | ||||
@@ -19,10 +19,13 @@ import bubble.resources.notify.SentNotificationsResource; | |||||
import bubble.server.BubbleConfiguration; | import bubble.server.BubbleConfiguration; | ||||
import bubble.service.account.StandardAccountMessageService; | import bubble.service.account.StandardAccountMessageService; | ||||
import bubble.service.account.download.AccountDownloadService; | import bubble.service.account.download.AccountDownloadService; | ||||
import bubble.service.boot.BubbleModelSetupService; | |||||
import bubble.service.cloud.StandardNetworkService; | import bubble.service.cloud.StandardNetworkService; | ||||
import com.fasterxml.jackson.databind.JsonNode; | import com.fasterxml.jackson.databind.JsonNode; | ||||
import lombok.Cleanup; | import lombok.Cleanup; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import org.cobbzilla.util.io.FileUtil; | |||||
import org.cobbzilla.util.io.TempDir; | |||||
import org.cobbzilla.util.string.LocaleUtil; | import org.cobbzilla.util.string.LocaleUtil; | ||||
import org.cobbzilla.wizard.auth.ChangePasswordRequest; | import org.cobbzilla.wizard.auth.ChangePasswordRequest; | ||||
import org.cobbzilla.wizard.client.ApiClientBase; | import org.cobbzilla.wizard.client.ApiClientBase; | ||||
@@ -32,6 +35,7 @@ import org.cobbzilla.wizard.client.script.ApiRunnerListenerStreamLogger; | |||||
import org.cobbzilla.wizard.client.script.ApiScript; | import org.cobbzilla.wizard.client.script.ApiScript; | ||||
import org.cobbzilla.wizard.model.HashedPassword; | import org.cobbzilla.wizard.model.HashedPassword; | ||||
import org.glassfish.grizzly.http.server.Request; | import org.glassfish.grizzly.http.server.Request; | ||||
import org.glassfish.jersey.media.multipart.FormDataParam; | |||||
import org.glassfish.jersey.server.ContainerRequest; | import org.glassfish.jersey.server.ContainerRequest; | ||||
import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
@@ -39,13 +43,16 @@ import org.springframework.stereotype.Service; | |||||
import javax.ws.rs.*; | import javax.ws.rs.*; | ||||
import javax.ws.rs.core.Context; | import javax.ws.rs.core.Context; | ||||
import javax.ws.rs.core.Response; | import javax.ws.rs.core.Response; | ||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.StringWriter; | import java.io.StringWriter; | ||||
import java.util.Locale; | import java.util.Locale; | ||||
import static bubble.ApiConstants.*; | import static bubble.ApiConstants.*; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.errorString; | import static org.cobbzilla.util.daemon.ZillaRuntime.errorString; | ||||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | |||||
import static org.cobbzilla.util.http.HttpContentTypes.TEXT_PLAIN; | |||||
import static org.cobbzilla.util.http.HttpContentTypes.*; | |||||
import static org.cobbzilla.util.json.JsonUtil.json; | import static org.cobbzilla.util.json.JsonUtil.json; | ||||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | import static org.cobbzilla.wizard.resources.ResourceUtil.*; | ||||
@@ -282,4 +289,21 @@ public class MeResource { | |||||
return ok(networkService.listLaunchStatuses(caller.getUuid())); | return ok(networkService.listLaunchStatuses(caller.getUuid())); | ||||
} | } | ||||
@Autowired private BubbleModelSetupService modelSetupService; | |||||
@POST @Path(EP_MODEL) | |||||
@Consumes(MULTIPART_FORM_DATA) | |||||
public Response uploadModel(@Context Request req, | |||||
@Context ContainerRequest ctx, | |||||
@FormDataParam("file") InputStream in, | |||||
@FormDataParam("name") String name) throws IOException { | |||||
final Account caller = userPrincipal(ctx); | |||||
if (empty(name)) return invalid("err.name.required"); | |||||
@Cleanup final TempDir temp = new TempDir(); | |||||
final File modelFile = new File(temp, name); | |||||
FileUtil.toFileOrDie(modelFile, in); | |||||
return ok(modelSetupService.setupModel(caller, modelFile)); | |||||
} | |||||
} | } |
@@ -136,11 +136,20 @@ public class BubbleConfiguration extends PgRestServerConfiguration | |||||
return bubbleJar; | return bubbleJar; | ||||
} | } | ||||
private static final AtomicReference<String> _DEFAULT_LOCALE = new AtomicReference<>(); | |||||
public static String getDEFAULT_LOCALE() { return _DEFAULT_LOCALE.get(); } | |||||
@Setter private String defaultLocale = DEFAULT_LOCALE; | @Setter private String defaultLocale = DEFAULT_LOCALE; | ||||
public String getDefaultLocale () { | public String getDefaultLocale () { | ||||
if (!empty(defaultLocale)) return defaultLocale; | |||||
if (!empty(defaultLocale)) { | |||||
if (_DEFAULT_LOCALE.get() == null) _DEFAULT_LOCALE.set(defaultLocale); | |||||
return defaultLocale; | |||||
} | |||||
final String[] allLocales = getAllLocales(); | final String[] allLocales = getAllLocales(); | ||||
if (ArrayUtils.contains(allLocales, DEFAULT_LOCALE)) return DEFAULT_LOCALE; | |||||
if (ArrayUtils.contains(allLocales, DEFAULT_LOCALE)) { | |||||
if (_DEFAULT_LOCALE.get() == null) _DEFAULT_LOCALE.set(DEFAULT_LOCALE); | |||||
return DEFAULT_LOCALE; | |||||
} | |||||
return allLocales[0]; | return allLocales[0]; | ||||
} | } | ||||
@@ -0,0 +1,24 @@ | |||||
package bubble.service.boot; | |||||
import bubble.model.account.HasAccount; | |||||
import bubble.server.BubbleConfiguration; | |||||
import lombok.Getter; | |||||
import org.cobbzilla.wizard.ModelSetupService; | |||||
import org.cobbzilla.wizard.model.Identifiable; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.stereotype.Service; | |||||
import static bubble.ApiConstants.ENTITY_CONFIGS_ENDPOINT; | |||||
@Service | |||||
public class BubbleModelSetupService extends ModelSetupService { | |||||
@Getter @Autowired private BubbleConfiguration configuration; | |||||
@Override protected String getEntityConfigsEndpoint() { return ENTITY_CONFIGS_ENDPOINT; } | |||||
@Override protected void setOwner(Identifiable account, Identifiable entity) { | |||||
if (entity instanceof HasAccount) ((HasAccount) entity).setAccount(account.getUuid()); | |||||
} | |||||
} |
@@ -29,6 +29,11 @@ label_menu_logout_icon=fa fa-sign-out-alt | |||||
# Model Setup fields | # Model Setup fields | ||||
form_title_model_setup=System Objects | form_title_model_setup=System Objects | ||||
field_label_entity_type=Object Type | field_label_entity_type=Object Type | ||||
button_label_upload_model=Upload Objects | |||||
field_label_upload_file=Objects File | |||||
field_description_upload_file=Must be a valid ZIP or TGZ archive file. | |||||
button_label_save_upload_model=Upload | |||||
button_label_close_upload_model=Cancel | |||||
button_label_add_entity=Add New | button_label_add_entity=Add New | ||||
button_label_close_add_entity=Cancel | button_label_close_add_entity=Cancel | ||||
button_label_save_add_entity=Save | button_label_save_add_entity=Save | ||||
@@ -446,3 +451,7 @@ err.user.noContact=No contact information provided for user | |||||
err.value.required=Value is required | err.value.required=Value is required | ||||
err.version.mismatch=Version in URL does not match version in object | err.version.mismatch=Version in URL does not match version in object | ||||
err.write.failed=Write operation failed for key | err.write.failed=Write operation failed for key | ||||
err.entity.classInFilename.invalid=Type of object could not be determined from filename. The part before the first dot or underscore should be a valid type name | |||||
err.entity.filenameExtension.invalid=The object file must be a JSON file containing one or more objects, or a Model Archive File (ending in .zip, .tar.gz, or .tgz) containing a manifest and corresponding object files. | |||||
err.entity.fileZipFormat.invalid=The Model Archive File was not in a readable format | |||||
err.entity.manifest.required=No manifest.json file was found within the Model Archive File |
@@ -21,7 +21,7 @@ | |||||
<!-- RedisService --> | <!-- RedisService --> | ||||
<context:component-scan base-package="org.cobbzilla.wizard.cache.redis"/> | <context:component-scan base-package="org.cobbzilla.wizard.cache.redis"/> | ||||
<!-- only load the model, DAOs and mocks of certain services --> | |||||
<!-- only load the modelFile, DAOs and mocks of certain services --> | |||||
<context:component-scan base-package="bubble.model"/> | <context:component-scan base-package="bubble.model"/> | ||||
<context:component-scan base-package="bubble.dao"> | <context:component-scan base-package="bubble.dao"> | ||||
<context:exclude-filter type="regex" expression="bubble.dao.SessionDAO"/> | <context:exclude-filter type="regex" expression="bubble.dao.SessionDAO"/> | ||||
@@ -1 +1 @@ | |||||
Subproject commit 5f88f3d53b4170f0183f65c83568b4b1756b6715 | |||||
Subproject commit fbb91b5da57408b03ce1c34de4e4d98da226ba13 |
@@ -1 +1 @@ | |||||
Subproject commit d6f4b31cda97819212ba2ff6b4ca34e9536d3a9a | |||||
Subproject commit d7ebbd7a8c490dae4c804425bdd83466220a69c8 |
@@ -1 +1 @@ | |||||
Subproject commit a1e5163cb506eab58296a04298694d9a4cf99230 | |||||
Subproject commit fb35a1d4e1ab1755365db87deef54b77f02cb369 |