Unverified Commit a164b837 authored by Alberto Nale's avatar Alberto Nale Committed by GitHub
Browse files

Merge branch 'master' into dataset-management-bugfix

parents 8f0900c0 23069d60
......@@ -167,14 +167,14 @@ public class PersistedTableHelper {
if (fieldValue == null || fieldValue.toString().isEmpty()) {
insertStatement.setNull(fieldIndex + 1, java.sql.Types.FLOAT);
} else {
insertStatement.setDouble(fieldIndex + 1, (Float) fieldValue);
insertStatement.setDouble(fieldIndex + 1, ((Number) fieldValue).floatValue());
}
} else if (fieldMetaTypeName.contains("Long")) {
// only for primitive type is necessary to use setNull method if value is null
if (fieldValue == null || fieldValue.toString().isEmpty()) {
insertStatement.setNull(fieldIndex + 1, java.sql.Types.BIGINT);
} else {
insertStatement.setLong(fieldIndex + 1, (Long) fieldValue);
insertStatement.setLong(fieldIndex + 1, ((Number) fieldValue).longValue());
}
} else if (fieldMetaTypeName.contains("Boolean")) {
// only for primitive type is necessary to use setNull method if value is null
......
......@@ -506,6 +506,16 @@ public class DataSetResource extends AbstractDataSetResource {
return super.deleteDataset(label);
}
@DELETE
@Path("/id/{id}")
@UserConstraint(functionalities = { SpagoBIConstants.SELF_SERVICE_DATASET_MANAGEMENT })
public Response deleteDatasetById(@PathParam("id") int id) {
IDataSetDAO datasetDao = DAOFactory.getDataSetDAO();
IDataSet dataset = datasetDao.loadDataSetById(id);
String label = dataset.getLabel();
return super.deleteDataset(label);
}
/**
* Delete a version for the selected dataset.
*
......
......@@ -1072,8 +1072,8 @@ public abstract class AbstractDataSetResource extends AbstractSpagoBIResource {
Response response = restClient.target(serviceUrlAsURL + "/knowage-data-preparation/api/1.0/instance/" + instanceId).request()
.header("X-Kn-Authorization", token).get();
JSONObject instance = new JSONObject(response.readEntity(String.class));
String sourceDsLabel = instance.getString("dataSetLabel");
deleteAvroFolder(sourceDsLabel);
int sourceDsId = instance.getInt("dataSetId");
deleteAvroFolder(sourceDsId);
// delete data preparation process instance
restClient.target(serviceUrlAsURL + "/knowage-data-preparation/api/1.0/instance/" + instanceId).request().header("X-Kn-Authorization", token)
.delete();
......@@ -1085,9 +1085,10 @@ public abstract class AbstractDataSetResource extends AbstractSpagoBIResource {
return Response.ok().build();
}
private void deleteAvroFolder(String label) {
private void deleteAvroFolder(int dsId) {
try {
Path avroExportFolder = Paths.get(SpagoBIUtilities.getResourcePath(), "dataPreparation", (String) getUserProfile().getUserId(), label);
Path avroExportFolder = Paths.get(SpagoBIUtilities.getResourcePath(), "dataPreparation", (String) getUserProfile().getUserId(),
Integer.toString(dsId));
Files.walkFileTree(avroExportFolder, new SimpleFileVisitor<Path>() {
// delete directories or folders
......
......@@ -662,8 +662,7 @@ public class DocumentExecutionResource extends AbstractSpagoBIResource {
boolean showParameterLov = true;
// Parameters NO TREE
if ("lov".equalsIgnoreCase(parameterUse.getValueSelection())
&& !objParameter.getSelectionType().equalsIgnoreCase(DocumentExecutionUtils.SELECTION_TYPE_TREE)) {
if ("lov".equalsIgnoreCase(parameterUse.getValueSelection())) {
ArrayList<HashMap<String, Object>> admissibleValues = objParameter.getAdmissibleValues();
......
......@@ -136,7 +136,7 @@ public class AvroExportJob extends AbstractExportJob {
try {
Class<?> type = dsMeta.getFieldType(i);
if (isDate(type)) {
value = dateFormatter.parse((String) value).getTime();
value = dateFormatter.parse(value.toString()).getTime();
} else if (isTimestamp(type)) {
value = DatabaseUtils.timestampFormatter(value);
......@@ -247,7 +247,8 @@ public class AvroExportJob extends AbstractExportJob {
@Override
protected OutputStream getDataOutputStream() {
try {
avroExportFolder = Paths.get(resourcePathAsStr, "dataPreparation", (String) userProfile.getUserId(), dataSet.getLabel());
String dsIdasString = Integer.toString(dataSet.getId());
avroExportFolder = Paths.get(resourcePathAsStr, "dataPreparation", (String) userProfile.getUserId(), dsIdasString);
Files.createDirectories(avroExportFolder);
return Files.newOutputStream(avroExportFolder.resolve(data));
} catch (Exception e) {
......
......@@ -401,13 +401,14 @@ public class DataSetResource {
*
*/
@GET
@Path("/advanced/{label}")
@Path("/advanced/{dsId}")
@Produces(MediaType.APPLICATION_JSON)
@UserConstraint(functionalities = { SpagoBIConstants.SELF_SERVICE_DATASET_MANAGEMENT })
public SbiDataSet getAdvancedDataSet(@PathParam("label") String label) {
public SbiDataSet getAdvancedDataSet(@PathParam("dsId") int dsId) {
try {
SbiDataSet dataSet = DAOFactory.getSbiDataSetDAO().loadSbiDataSetByLabel(label);
final UserProfile userProfile = getUserProfile();
SbiDataSet dataSet = DAOFactory.getSbiDataSetDAO().loadSbiDataSetByIdAndOrganiz(dsId, userProfile.getOrganization());
return dataSet;
} catch (Exception t) {
......@@ -424,8 +425,8 @@ public class DataSetResource {
@Path("/avro")
@Produces(MediaType.APPLICATION_JSON)
@UserConstraint(functionalities = { SpagoBIConstants.SELF_SERVICE_DATASET_MANAGEMENT })
public List<String> getPreparedDataSets() {
List<String> preparedDataSets = new ArrayList<String>();
public List<String> getAvroDataSets() {
List<String> avroDataSets = new ArrayList<String>();
try {
final UserProfile userProfile = getUserProfile();
java.nio.file.Path avroExportFolder = Paths.get(SpagoBIUtilities.getRootResourcePath(), userProfile.getOrganization(), "dataPreparation",
......@@ -434,14 +435,14 @@ public class DataSetResource {
for (int i = 0; i < datasets.length; i++) {
boolean avroReady = new File(datasets[i], "ready").exists();
if (avroReady) {
preparedDataSets.add(datasets[i].getName());
avroDataSets.add(datasets[i].getName());
}
}
} catch (Exception e) {
logger.error("Cannot get list of prepared datasets", e);
logger.error("Cannot get list of Avro datasets", e);
return new ArrayList<String>();
}
return preparedDataSets;
return avroDataSets;
}
/**
......
......@@ -40,8 +40,11 @@ import it.eng.spago.base.SourceBeanAttribute;
import it.eng.spago.configuration.ConfigSingleton;
import it.eng.spago.error.EMFInternalError;
import it.eng.spago.security.IEngUserProfile;
import it.eng.spagobi.analiticalmodel.document.bo.BIObject;
import it.eng.spagobi.analiticalmodel.document.dao.IBIObjectDAO;
import it.eng.spagobi.commons.SingletonConfig;
import it.eng.spagobi.commons.constants.SpagoBIConstants;
import it.eng.spagobi.commons.dao.DAOFactory;
import it.eng.spagobi.commons.serializer.SerializationException;
import it.eng.spagobi.commons.serializer.Serializer;
import it.eng.spagobi.commons.utilities.DocumentUtilities;
......@@ -774,11 +777,29 @@ public class MenuListJSONSerializerForREST implements Serializer {
}
private void setPropertiesForObjectMenu(Menu childElem, JSONObject temp2, String path) throws JSONException {
if (childElem.isClickable() == true) {
temp2.put(TO, contextName + "/servlet/AdapterHTTP?ACTION_NAME=MENU_BEFORE_EXEC&MENU_ID=" + childElem.getMenuId());
} else {
temp2.put("isClickable", "false");
IBIObjectDAO dao = DAOFactory.getBIObjectDAO();
try {
BIObject document = dao.loadBIObjectById(childElem.getObjId());
String documentLink = getDocumentLink(document);
if (childElem.isClickable() == true) {
temp2.put(TO, documentLink);
} else {
temp2.put("isClickable", "false");
}
} catch (Exception e) {
logger.error("Cannot load menu item for document: " + childElem.getObjId(), e);
}
}
private String getDocumentLink(BIObject document) {
String documentLabel = document.getLabel();
String enginePath;
if (document.getEngineLabel() != null && document.getEngineLabel().equals("knowageolapengine"))
enginePath = "olap";
else
enginePath = "document-composite";
return "/document-browser/" + enginePath + "/" + documentLabel;
}
private void setPropertiesForAdminWithUrlMenu(Menu childElem, Locale locale, JSONObject temp2, String path) throws JSONException {
......
......@@ -653,8 +653,8 @@ public class DetailMenuModule extends AbstractHttpModule {
}
} else if (functionality.equals(SpagoBIConstants.WORKSPACE_MANAGEMENT)) {
String initialPath = menu.getInitialPath();
if (initialPath != null && !initialPath.trim().equals("")) {
url += "&currentOptionMainMenu" + "=" + initialPath;
if (initialPath != null && initialPath.equals("documents")) {
url += "/recent";
}
}
......
import App from './App.vue'
import { shallowMount } from '@vue/test-utils'
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
locale: { country: 'IT', language: 'it' }
}
}
})
describe('App', () => {
test('uses mounts', () => {
const wrapper = shallowMount(App, {
global: {
plugins: [store],
stubs: ['router-link', 'router-view'],
mocks: {
$t: (msg) => msg
}
}
})
expect(typeof wrapper.data).toBe('undefined')
})
})
......@@ -21,6 +21,7 @@
import { mapState } from 'vuex'
import WEB_SOCKET from '@/services/webSocket.js'
import themeHelper from '@/helpers/themeHelper/themeHelper'
import { primeVueDate, getLocale } from '@/helpers/commons/localeHelper'
export default defineComponent({
components: { ConfirmDialog, KnOverlaySpinnerPanel, MainMenu, Toast },
......@@ -49,9 +50,17 @@
}
localStorage.setItem('locale', storedLocale)
localStorage.setItem('token', response.data.userUniqueIdentifier)
store.commit('setLocale', storedLocale)
this.$i18n.locale = storedLocale
// @ts-ignore
if (this.$i18n.messages[this.$i18n.locale.replaceAll('-', '_')]) {
// @ts-ignore
this.$primevue.config.locale = { ...this.$primevue.config.locale, ...this.$i18n.messages[this.$i18n.locale.replaceAll('-', '_')].locale }
}
this.$primevue.config.locale.dateFormat = primeVueDate(getLocale(true))
let language = this.$i18n
let splittedLanguage = language.locale.split('_')
......
import { mount } from '@vue/test-utils'
import Avatar from 'primevue/avatar'
import Badge from 'primevue/badge'
import Button from 'primevue/button'
import KnListButtonRenderer from './KnListButtonRenderer.vue'
import Listbox from 'primevue/listbox'
import Menu from 'primevue/menu'
import KnListBox from './KnListBox.vue'
import PrimeVue from 'primevue/config'
const mockedOptions = [
{ id: 1, label: 'label1', name: 'Name1', description: 'Desc1' },
{ id: 2, label: 'label2', name: 'Name2', description: 'Desc2' },
{ id: 3, label: 'label3', name: 'Name3', description: 'Desc3' }
{
id: 544,
name: '/albnale/admin',
description: '/albnale/admin',
roleTypeCD: 'ADMIN',
code: null,
roleTypeID: 32,
organization: 'DEMO',
isPublic: false
},
{
id: 450,
name: '/demo/admin',
description: '/demo/admin',
roleTypeCD: 'ADMIN',
code: null,
roleTypeID: 32,
organization: 'DEMO',
isPublic: false
},
{
id: 455,
name: '/demo/user',
description: '/demo/user',
roleTypeCD: 'USER',
code: '/demo/user',
roleTypeID: 31,
organization: 'DEMO',
isPublic: false
}
]
const mockedsettings = {
defaultSortField: 'name',
interaction: {
parameterLabel: 'id',
parameterValue: 'id',
path: 'navigation-detail',
type: 'router'
},
const mockedSettings = {
buttons: [
{
emits: 'delete',
icon: 'fas fa-trash-alt',
label: 'common.delete'
}
]
],
defaultSortField: 'name',
filterFields: ['name'],
interaction: {
type: 'event'
},
sortFields: ['name'],
textField: 'roleTypeCD',
titleField: 'name'
}
const $confirm = {
......@@ -41,7 +71,7 @@ const factory = () => {
return mount(KnListBox, {
props: {
options: mockedOptions,
settings: mockedsettings
settings: mockedSettings
},
global: {
directives: {
......@@ -51,6 +81,8 @@ const factory = () => {
stubs: {
Avatar,
Badge,
Button,
KnListButtonRenderer,
Listbox,
Menu
},
......@@ -63,7 +95,7 @@ const factory = () => {
})
}
describe('Cross-navigation Management loading', () => {
describe('KnListBox loading', () => {
it('the list shows an hint component when loaded empty', async () => {
const wrapper = factory()
......@@ -73,12 +105,46 @@ describe('Cross-navigation Management loading', () => {
expect(wrapper.find('[data-test="list"]').html()).toContain('No available options')
})
})
describe('Cross-navigation Management', () => {
it('shows a prompt when user click on a list item delete button to delete it', async () => {
describe('KnListBox', () => {
it('shows list of items', async () => {
const wrapper = factory()
await wrapper.find('[data-test="list-item"]').trigger('click')
expect(wrapper.html()).toContain('/albnale/admin')
expect(wrapper.html()).toContain('/demo/admin')
expect(wrapper.html()).toContain('/demo/user')
})
it('emits event with the clicked item on click', async () => {
const wrapper = factory()
console.log(wrapper.html())
const deleteButton = wrapper.find('[data-test="delete-button-0"]')
await deleteButton.trigger('click')
await wrapper.find('[data-test="list-item"]').trigger('click')
expect(wrapper.emitted()['click'][0][0].item).toStrictEqual(mockedOptions[0])
})
it('filters the list', async () => {
const wrapper = factory()
const searchInput = wrapper.find('.p-inputtext')
expect(wrapper.html()).toContain('/albnale/admin')
expect(wrapper.html()).toContain('/demo/admin')
expect(wrapper.html()).toContain('/demo/user')
await searchInput.setValue('/albnale/admin')
expect(wrapper.html()).toContain('/albnale/admin')
expect(wrapper.html()).not.toContain('/demo/admin')
expect(wrapper.html()).not.toContain('/demo/user')
})
it('emitts event with item to delete', async () => {
const wrapper = factory()
const searchInput = wrapper.find('.p-inputtext')
await searchInput.setValue('/demo/admin')
await wrapper.find('[data-test="delete-button-0"]').trigger('click')
expect(wrapper.emitted().delete[0][0].item).toStrictEqual(mockedOptions[1])
})
it('shows the detail when clicking on a item', () => {})
})
import { mount } from '@vue/test-utils'
import Button from 'primevue/button'
import KnListButtonRenderer from './KnListButtonRenderer.vue'
import Menu from 'primevue/menu'
import PrimeVue from 'primevue/config'
const mockedButtons = [
{ emits: 'clone', icon: 'far fa-copy', label: 'common.clone' },
{
emits: 'delete',
icon: 'fas fa-trash-alt',
label: 'common.delete'
}
]
const $confirm = {
require: jest.fn()
}
const $store = {
commit: jest.fn()
}
const factory = () => {
return mount(KnListButtonRenderer, {
props: {
buttons: mockedButtons,
selectedItem: {}
},
global: {
directives: {
tooltip() {}
},
plugins: [PrimeVue],
stubs: {
Button,
Menu
},
mocks: {
$t: (msg) => msg,
$store,
$confirm
}
}
})
}
describe('KnListButtonRenderer', () => {
it('loads buttons properly', async () => {
const wrapper = factory()
expect(wrapper.vm.filteredButtons).toStrictEqual(mockedButtons)
})
})
......@@ -468,10 +468,6 @@ describe('Parameter Sidebar - Document has parameters', () => {
expect(wrapper.vm.parameters.filterStatus[1].driverDefaultValue).toStrictEqual([{ value: '5', desc: '5' }])
expect(wrapper.find('[data-test="parameter-input-7635"]').wrapperElement._value).toBe('5')
expect(wrapper.vm.parameters.filterStatus[2].type).toBe('DATE')
expect(wrapper.vm.parameters.filterStatus[2].driverDefaultValue).toStrictEqual([{ value: '01/01/2002', desc: '2002-01-01#yyyy-mm-dd' }])
expect(wrapper.find('[data-test="parameter-date-input-7636"]').wrapperElement._value).toBe('01/01/2002')
expect(wrapper.vm.parameters.filterStatus[0].selectionType).toBe('LIST')
expect(wrapper.vm.parameters.filterStatus[0].driverDefaultValue).toStrictEqual([{ _col0: 'Non-Consumable', _col1: '0' }])
expect(wrapper.vm.selectedParameterCheckbox[7632]).toStrictEqual(['Non-Consumable'])
......
......@@ -26,11 +26,6 @@ export async function updateDataDependency(loadedParameters: { filterStatus: iPa
export async function dataDependencyCheck(loadedParameters: { filterStatus: iParameter[], isReadyForExecution: boolean }, parameter: iParameter, loading: boolean, document: any, sessionRole: string, $http: any, mode: string) {
loading = true
if (parameter.parameterValue[0]) {
parameter.parameterValue[0] = { value: '', description: '' }
} else {
parameter.parameterValue = [{ value: '', description: '' }]
}
const postData = { label: document?.label, parameters: getFormattedParameters(loadedParameters), paramId: parameter.urlName, role: sessionRole }
let url = '2.0/documentExeParameters/admissibleValues'
......@@ -48,7 +43,10 @@ export async function dataDependencyCheck(loadedParameters: { filterStatus: iPar
}
export function formatParameterAfterDataDependencyCheck(parameter: any) {
parameter.parameterValue = parameter.multivalue ? [] : [{ value: '', description: '' }]
if (!checkIfParameterDataContainsNewValue(parameter)) {
parameter.parameterValue = parameter.multivalue ? [] : [{ value: '', description: '' }]
}
if (parameter.data) {
parameter.data = parameter.data.map((data: any) => {
return formatParameterDataOptions(parameter, data)
......@@ -92,3 +90,25 @@ export function getFormattedParameters(loadedParameters: { filterStatus: iParame
return parameters
}
function checkIfParameterDataContainsNewValue(parameter: iParameter) {
const valueColumn = parameter.metadata.valueColumn
const descriptionColumn = parameter.metadata.descriptionColumn
let valueIndex = null as any
if (parameter.metadata.colsMap) {
valueIndex = Object.keys(parameter.metadata.colsMap).find((key: string) => parameter.metadata.colsMap[key] === valueColumn)
}
let descriptionIndex = null as any
if (parameter.metadata.colsMap) {
descriptionIndex = Object.keys(parameter.metadata.colsMap).find((key: string) => parameter.metadata.colsMap[key] === descriptionColumn)
}
const index = parameter.data.findIndex((option: any) => {
if (option.value || option.description) {
return parameter.parameterValue[0].value === option.value && parameter.parameterValue[0].description === option.description
} else {
return parameter.parameterValue[0].value === option[valueIndex] && parameter.parameterValue[0].description === option[descriptionIndex]
}
})
return index !== -1;
}
\ No newline at end of file
......@@ -33,7 +33,8 @@
><template #option="slotProps">
<div class="p-text-uppercase kn-list-item fieldType" draggable="true" @dragstart="dragElement($event, slotProps.option, 'field')">
<div><i class="fa fa-solid fa-bars"></i></div>
<div class="p-ml-2">{{ slotProps.option.fieldAlias }}</div>
<div v-if="source === 'QBE'" class="p-ml-2">{{ slotProps.option.fieldLabel }}</div>
<div v-else class="p-ml-2">{{ slotProps.option.fieldAlias }}</div>
</div>
</template></Listbox
>
......@@ -107,7 +108,8 @@ export default defineComponent({
readOnly: Boolean,
descriptor: Object,
template: {} as any,
valid: Boolean
valid: Boolean,
source: String
},
data() {
return {
......@@ -156,7 +158,7 @@ export default defineComponent({
}
}
if (!this.readOnly && this.template && !this.template.parameters) {
if (!this.readOnly && this.template && !this.template.parameters && this.source === 'QBE') {
this.cf = { colName: this.template.alias, formula: this.template.expression } as IKnCalculatedField
}
},
......
{
"af-ZA": "yyyy/MM/dd",
"am-ET": "d/M/yyyy",
"ar-AE": "dd/MM/yyyy",
"ar-BH": "dd/MM/yyyy",
"ar-DZ": "dd-MM-yyyy",
"ar-EG": "dd/MM/yyyy",
"ar-IQ": "dd/MM/yyyy",
"ar-JO": "dd/MM/yyyy",
"ar-KW": "dd/MM/yyyy",
"ar-LB": "dd/MM/yyyy",
"ar-LY": "dd/MM/yyyy",
"ar-MA": "dd-MM-yyyy",