package io.cattle.platform.api.settings.manager;
import static io.cattle.platform.core.model.tables.SettingTable.*;
import io.cattle.platform.api.resource.jooq.AbstractJooqResourceManager;
import io.cattle.platform.api.settings.model.ActiveSetting;
import io.cattle.platform.archaius.sources.NamedConfigurationSource;
import io.cattle.platform.archaius.util.ArchaiusUtil;
import io.cattle.platform.core.model.Setting;
import io.cattle.platform.util.type.CollectionUtils;
import io.github.ibuildthecloud.gdapi.context.ApiContext;
import io.github.ibuildthecloud.gdapi.exception.ClientVisibleException;
import io.github.ibuildthecloud.gdapi.factory.SchemaFactory;
import io.github.ibuildthecloud.gdapi.id.IdFormatter;
import io.github.ibuildthecloud.gdapi.model.ListOptions;
import io.github.ibuildthecloud.gdapi.model.Resource;
import io.github.ibuildthecloud.gdapi.model.impl.ResourceImpl;
import io.github.ibuildthecloud.gdapi.request.ApiRequest;
import io.github.ibuildthecloud.gdapi.url.UrlBuilder;
import io.github.ibuildthecloud.gdapi.util.ResponseCodes;
import io.github.ibuildthecloud.gdapi.util.TypeUtils;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.collections.Predicate;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.config.ConcurrentCompositeConfiguration;
import com.netflix.config.DynamicConfiguration;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringListProperty;
public class SettingManager extends AbstractJooqResourceManager {
private static final Logger log = LoggerFactory.getLogger(SettingManager.class);
private static final DynamicStringListProperty PUBLIC_SETTINGS = ArchaiusUtil.getList("settings.public");
@Override
public Class<?>[] getTypeClasses() {
return new Class<?>[] { Setting.class, ActiveSetting.class };
}
@Override
protected Object createInternal(String type, ApiRequest request) {
Setting setting = request.proxyRequestObject(Setting.class);
Object existing = getByIdInternal(type, setting.getName(), new ListOptions());
if ( existing instanceof ActiveSetting && ((ActiveSetting)existing).isInDb() ) {
return updateInternal(type, setting.getName(), existing, request);
} else {
return super.createInternal(type, request);
}
}
@Override
protected Object updateInternal(String type, String id, Object obj, ApiRequest request) {
type = request.getSchemaFactory().getSchemaName(Setting.class);
Object result = null;
if (obj instanceof ActiveSetting) {
Setting setting = ((ActiveSetting) obj).getSetting();
if (setting == null) {
String settingType = request.getSchemaFactory().getSchemaName(Setting.class);
result = doCreate(settingType, Setting.class, CollectionUtils.asMap((Object) SETTING.NAME, ((ActiveSetting) obj).getName(), SETTING.VALUE,
request.proxyRequestObject(Setting.class).getValue()));
} else {
obj = setting;
}
}
if (result == null) {
result = super.updateInternal(type, id, obj, request);
}
return result == null ? null : getByIdInternal(type, id, new ListOptions());
}
@Override
protected Object getByIdInternal(String type, String id, ListOptions options) {
type = ApiContext.getSchemaFactory().getSchemaName(Setting.class);
Setting setting = (Setting) super.getByIdInternal(type, id, options);
Configuration config = lookupConfiguration();
if (config == null) {
return setting;
}
if (setting == null) {
return getSettingByName(id, config, true);
} else {
ActiveSetting activeSetting = getSettingByName(setting.getName(), config, true);
activeSetting.setSetting(setting);
return activeSetting;
}
}
@Override
protected Object removeFromStore(String type, String id, Object obj, ApiRequest request) {
if (obj instanceof ActiveSetting) {
id = ((ActiveSetting) obj).getId();
} else if (obj instanceof Setting) {
Long idL = ((Setting) obj).getId();
id = idL == null ? id : idL.toString();
}
if (id == null) {
return null;
}
try {
int result = create().delete(SETTING).where(SETTING.ID.eq(new Long(id))).execute();
if (result != 1) {
log.error("While deleting type [{}] and id [{}] got a result of [{}]", type, id, result);
throw new ClientVisibleException(ResponseCodes.CONFLICT);
}
if (obj instanceof ActiveSetting) {
return getByIdInternal(type, ((ActiveSetting) obj).getName(), new ListOptions());
} else {
return obj;
}
} catch (NumberFormatException nfe) {
return null;
}
}
@SuppressWarnings("unchecked")
@Override
protected Object listInternal(SchemaFactory schemaFactory, String type, Map<Object, Object> criteria, ListOptions options) {
type = schemaFactory.getSchemaName(Setting.class);
Configuration config = lookupConfiguration();
if (config == null) {
return super.listInternal(schemaFactory, type, criteria, options);
}
Object list = super.listInternal(schemaFactory, type, criteria, options);
if (criteria.containsKey(TypeUtils.ID_FIELD)) {
return list;
}
// TODO, need to make filtering on name work
String value = null;
List<ActiveSetting> result = new ArrayList<ActiveSetting>();
for (ActiveSetting setting : getSettings((List<Setting>) CollectionUtils.toList(list), config)) {
if (value == null) {
result.add(setting);
} else if (value.equals(setting.getName())) {
result.add(setting);
}
}
return result;
}
@Override
protected Object authorize(Object object) {
Predicate predicate = new SettingsFilter(PUBLIC_SETTINGS.get(), ApiContext.getContext().getApiRequest());
if (object instanceof List<?>) {
List<Object> list = new ArrayList<>((List<?>) object);
org.apache.commons.collections.CollectionUtils.filter(list, predicate);
return super.authorize(list);
} else if (predicate.evaluate(object)){
return super.authorize(object);
}
return null;
}
protected List<ActiveSetting> getSettings(List<Setting> settings, Configuration config) {
Map<String, ActiveSetting> result = new TreeMap<String, ActiveSetting>();
for (Setting setting : settings) {
ActiveSetting activeSetting = getSettingByName(setting.getName(), config, false);
activeSetting.setSetting(setting);
result.put(activeSetting.getName(), activeSetting);
}
Iterator<String> iter = config.getKeys();
while (iter.hasNext()) {
String key = iter.next();
if (result.containsKey(key) || !key.matches("^[a-z].*")) {
/*
* The regexp check is specifically to avoid showing environment
* variables. Those tend to have sensitive info in them
*/
continue;
}
ActiveSetting activeSetting = getSettingByName(key, config, false);
if (activeSetting != null) {
result.put(activeSetting.getName(), activeSetting);
}
}
return new ArrayList<ActiveSetting>(result.values());
}
protected ActiveSetting getSettingByName(String name, Configuration config, boolean checkDb) {
if (name == null) {
return null;
}
Object value = config.getProperty(name);
Configuration source = null;
if (config instanceof ConcurrentCompositeConfiguration) {
source = ((ConcurrentCompositeConfiguration) config).getSource(name);
} else if (config instanceof CompositeConfiguration) {
source = ((CompositeConfiguration) config).getSource(name);
}
if (value != null) {
value = value.toString();
}
ActiveSetting activeSetting = new ActiveSetting(name, value, toString(source));
if (checkDb) {
Setting setting = create().selectFrom(SETTING).where(SETTING.NAME.eq(name)).fetchAny();
if (setting != null) {
activeSetting.setSetting(setting);
}
}
return activeSetting;
}
protected String toString(Configuration config) {
if (config instanceof NamedConfigurationSource) {
return ((NamedConfigurationSource) config).getSourceName();
}
if (config instanceof DynamicConfiguration) {
return ((DynamicConfiguration) config).getSource().getClass().getName();
}
return config == null ? null : config.getClass().getName();
}
protected Configuration lookupConfiguration() {
Object obj = DynamicPropertyFactory.getBackingConfigurationSource();
if (obj instanceof Configuration) {
return (Configuration) obj;
}
return null;
}
@Override
public String[] getTypes() {
return new String[0];
}
@Override
protected Resource createResource(Object obj, IdFormatter idFormatter, ApiRequest apiRequest) {
Resource r = super.createResource(obj, idFormatter, apiRequest);
if (r instanceof ResourceImpl) {
((ResourceImpl) r).setType("setting");
Object id = r.getFields().get("name");
if (id != null) {
((ResourceImpl) r).setId(id.toString());
}
r.getLinks().remove(UrlBuilder.SELF);
// Recreate the SELF url
r.getLinks();
((ResourceImpl) r).setType("activeSetting");
((ResourceImpl) r).getFields().put("baseType", "setting");
}
return r;
}
}