From c4c68d45ca6e86a9a7e86e652d1c8535384676b5 Mon Sep 17 00:00:00 2001 From: XingY Date: Thu, 8 May 2025 09:48:03 -0700 Subject: [PATCH 01/30] Support auditUserComment for domain update/delete --- .../gwt/client/assay/model/GWTProtocol.java | 12 ++++++++ .../audit/query/AbstractAuditDomainKind.java | 4 +-- .../labkey/api/exp/api/ExperimentService.java | 3 +- .../api/exp/api/SampleTypeDomainKind.java | 8 ++--- .../labkey/api/exp/api/SampleTypeService.java | 2 +- .../api/exp/property/AbstractDomainKind.java | 4 +-- .../org/labkey/api/exp/property/Domain.java | 4 +-- .../labkey/api/exp/property/DomainKind.java | 11 +++---- .../labkey/api/exp/property/DomainUtil.java | 10 +++++-- .../api/exp/property/TestDomainKind.java | 4 +-- .../AbstractIssuesListDefDomainKind.java | 8 ++--- .../api/query/ExtendedTableDomainKind.java | 4 +-- .../api/reports/model/ReportPropsManager.java | 2 +- .../PlateBasedAssaySampleTypeDomainKind.java | 4 +-- .../plate/AssayPlateMetadataServiceImpl.java | 2 +- .../org/labkey/assay/plate/PlateManager.java | 4 +-- .../assay/plate/PlateMetadataDomainKind.java | 2 +- core/package-lock.json | 16 +++++----- core/package.json | 2 +- .../DataClassDesigner/DataClassDesigner.tsx | 2 +- .../SampleTypeDesigner/SampleTypeDesigner.tsx | 2 +- .../labkey/core/query/UsersDomainKind.java | 4 +-- experiment/package-lock.json | 16 +++++----- experiment/package.json | 2 +- .../experiment/api/DataClassDomainKind.java | 9 +++--- .../experiment/api/ExpDataClassImpl.java | 2 +- .../experiment/api/ExpSampleTypeImpl.java | 4 +-- .../experiment/api/ExperimentServiceImpl.java | 7 +++-- .../experiment/api/SampleTypeServiceImpl.java | 8 ++--- .../experiment/api/VocabularyDomainKind.java | 4 +-- .../experiment/api/property/DomainImpl.java | 16 +++++----- .../property/PropertyController.java | 29 +++++++++++++------ .../org/labkey/issue/model/IssueManager.java | 2 +- .../labkey/list/model/ListDefinitionImpl.java | 2 +- .../org/labkey/list/model/ListDomainKind.java | 8 ++--- .../org/labkey/list/model/ListImporter.java | 4 +-- .../model/AbstractSpecimenDomainKind.java | 4 +-- .../specimen/model/SpecimenDomainKind.java | 4 +-- .../model/SpecimenEventDomainKind.java | 4 +-- .../model/SpecimenTablesProvider.java | 2 +- .../api/specimen/model/VialDomainKind.java | 5 ++-- .../study/assay/StudyPublishManager.java | 6 ++-- .../study/controllers/StudyController.java | 2 +- .../StudyDefinitionController.java | 2 +- .../dataset/DatasetSnapshotProvider.java | 2 +- .../labkey/study/model/DatasetDomainKind.java | 4 +-- 46 files changed, 148 insertions(+), 114 deletions(-) diff --git a/api/gwtsrc/org/labkey/api/gwt/client/assay/model/GWTProtocol.java b/api/gwtsrc/org/labkey/api/gwt/client/assay/model/GWTProtocol.java index ff4cf8dc35b..8d94f46b7db 100644 --- a/api/gwtsrc/org/labkey/api/gwt/client/assay/model/GWTProtocol.java +++ b/api/gwtsrc/org/labkey/api/gwt/client/assay/model/GWTProtocol.java @@ -76,6 +76,7 @@ public class GWTProtocol implements IsSerializable private boolean _plateMetadata; private String _status; private List _excludedContainerIds; + private String _auditUserComment; public GWTProtocol() { @@ -436,4 +437,15 @@ public void setExcludedContainerIds(List excludedContainerIds) { _excludedContainerIds = excludedContainerIds; } + + public String getAuditUserComment() + { + return _auditUserComment; + } + + public void setAuditUserComment(String auditUserComment) + { + _auditUserComment = auditUserComment; + } + } diff --git a/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java b/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java index d21f0436ed3..f66ce6778f1 100644 --- a/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java +++ b/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java @@ -248,13 +248,13 @@ public Domain createDomain(GWTDomain domain, JSONObject arguments, Container con @Override public ValidationException updateDomain(GWTDomain original, GWTDomain update, - JSONObject options, Container container, User user, boolean includeWarnings) + JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) { throw new UnsupportedOperationException(); } @Override - public void deleteDomain(User user, Domain domain) + public void deleteDomain(User user, Domain domain, String auditUserComment) { throw new UnsupportedOperationException(); } diff --git a/api/src/org/labkey/api/exp/api/ExperimentService.java b/api/src/org/labkey/api/exp/api/ExperimentService.java index 0a03f3a7072..2ccdfde25da 100644 --- a/api/src/org/labkey/api/exp/api/ExperimentService.java +++ b/api/src/org/labkey/api/exp/api/ExperimentService.java @@ -279,7 +279,8 @@ ValidationException updateDataClass( @NotNull ExpDataClass dataClass, @Nullable DataClassDomainKindProperties options, GWTDomain original, - GWTDomain update + GWTDomain update, + String auditUserComment ); /** diff --git a/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java b/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java index c59da1b3c33..1b85d29d89b 100644 --- a/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java +++ b/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java @@ -394,9 +394,9 @@ public DefaultValueType[] getDefaultValueOptions(Domain domain) @Override @NotNull public ValidationException updateDomain(GWTDomain original, @NotNull GWTDomain update, - @Nullable SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings) + @Nullable SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, String auditUserComment) { - return SampleTypeService.get().updateSampleType(original, update, options, container, user, includeWarnings); + return SampleTypeService.get().updateSampleType(original, update, options, container, user, includeWarnings, auditUserComment); } @Override @@ -606,13 +606,13 @@ public Domain createDomain(GWTDomain domain, @Nullable SampleTypeDomainKindPrope } @Override - public void deleteDomain(User user, Domain domain) + public void deleteDomain(User user, Domain domain, String auditUserComment) { ExpSampleType st = SampleTypeService.get().getSampleType(domain.getTypeURI()); if (st == null) throw new NotFoundException("Sample Type not found: " + domain); - st.delete(user); + st.delete(user, auditUserComment); } @Override diff --git a/api/src/org/labkey/api/exp/api/SampleTypeService.java b/api/src/org/labkey/api/exp/api/SampleTypeService.java index 2c15e0ab098..b38ebf178b4 100644 --- a/api/src/org/labkey/api/exp/api/SampleTypeService.java +++ b/api/src/org/labkey/api/exp/api/SampleTypeService.java @@ -230,7 +230,7 @@ default Map incrementSampleCounts(@Nullable Date counterDate) long getProjectRootSampleCount(Container container); - ValidationException updateSampleType(GWTDomain original, GWTDomain update, SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings); + ValidationException updateSampleType(GWTDomain original, GWTDomain update, SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, String auditUserComment); void addAuditEvent(User user, Container container, String comment, String userComment, ExpMaterial sample, Map metadata); diff --git a/api/src/org/labkey/api/exp/property/AbstractDomainKind.java b/api/src/org/labkey/api/exp/property/AbstractDomainKind.java index 5f7436bfb08..22c59f1a2cc 100644 --- a/api/src/org/labkey/api/exp/property/AbstractDomainKind.java +++ b/api/src/org/labkey/api/exp/property/AbstractDomainKind.java @@ -123,13 +123,13 @@ public Domain createDomain(GWTDomain domain, T arguments, @Override @NotNull public ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable T options, Container container, User user, boolean includeWarnings) + @Nullable T options, Container container, User user, boolean includeWarnings, String auditUserComment) { return DomainUtil.updateDomainDescriptor(original, update, container, user); } @Override - public void deleteDomain(User user, Domain domain) + public void deleteDomain(User user, Domain domain, String auditUserComment) { } diff --git a/api/src/org/labkey/api/exp/property/Domain.java b/api/src/org/labkey/api/exp/property/Domain.java index 687bc171add..22f94fd99c2 100644 --- a/api/src/org/labkey/api/exp/property/Domain.java +++ b/api/src/org/labkey/api/exp/property/Domain.java @@ -88,8 +88,8 @@ default void delete(@Nullable User user, @Nullable String auditUserComment) thro delete(user); } void save(User user) throws ChangePropertyDescriptorException; - void save(User user, boolean auditComment) throws ChangePropertyDescriptorException; - void save(User user, @Nullable String allowAddBaseProperty) throws ChangePropertyDescriptorException; + void save(User user, boolean allowAddBaseProperty) throws ChangePropertyDescriptorException; + void save(User user, @Nullable String auditComment, @Nullable String auditUserComment) throws ChangePropertyDescriptorException; /** Returns true if this domain has not yet been saved. */ boolean isNew(); diff --git a/api/src/org/labkey/api/exp/property/DomainKind.java b/api/src/org/labkey/api/exp/property/DomainKind.java index 462cc0ebe30..fd3155ea977 100644 --- a/api/src/org/labkey/api/exp/property/DomainKind.java +++ b/api/src/org/labkey/api/exp/property/DomainKind.java @@ -160,18 +160,19 @@ public Set getReservedPropertyNamePrefixes() * @param options Any domain kind specific properties/options. * @param container Container * @param user User + * @param includeWarnings + * @param auditUserComment * @return A list of errors collected during the update. */ abstract public ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable T options, Container container, User user, boolean includeWarnings); - + @Nullable T options, Container container, User user, boolean includeWarnings, String auditUserComment); /** * Delete a Domain and its associated data. - * @param domain - * @param user * @param domain The domain to delete + * @param user + * @param auditUserComment */ - abstract public void deleteDomain(User user, Domain domain); + abstract public void deleteDomain(User user, Domain domain, String auditUserComment); /** * Get base properties defined for that domainkind. The domain parameter is only when there may be a condition diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index ea78a9f7683..3de5b1359ef 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -744,9 +744,15 @@ public static ValidationException updateDomainDescriptor(GWTDomain orig, GWTDomain update, Container container, User user, boolean updateDomainName, @Nullable String auditComment) + { + return updateDomainDescriptor(orig, update, container, user, updateDomainName, auditComment, null); + } + + /** @return Errors encountered during the save attempt */ + @NotNull + public static ValidationException updateDomainDescriptor(GWTDomain orig, GWTDomain update, Container container, User user, boolean updateDomainName, @Nullable String auditComment, @Nullable String auditUserComment) { LOG.info("Updating domain descriptor for " + orig.getName()); assert orig.getDomainURI().equals(update.getDomainURI()); @@ -908,7 +914,7 @@ public static ValidationException updateDomainDescriptor(GWTDomain(defaultValues); try diff --git a/api/src/org/labkey/api/exp/property/TestDomainKind.java b/api/src/org/labkey/api/exp/property/TestDomainKind.java index 4a230d9be59..58e257123f1 100644 --- a/api/src/org/labkey/api/exp/property/TestDomainKind.java +++ b/api/src/org/labkey/api/exp/property/TestDomainKind.java @@ -172,13 +172,13 @@ public Domain createDomain(GWTDomain domain, JSONObject arguments, Container con @Override public ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable JSONObject options, Container container, User user, boolean includeWarnings) + @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) { throw new UnsupportedOperationException(); } @Override - public void deleteDomain(User user, Domain domain) + public void deleteDomain(User user, Domain domain, String auditUserComment) { throw new UnsupportedOperationException(); } diff --git a/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java b/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java index e8453b11f6f..5b3bea29cbb 100644 --- a/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java +++ b/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java @@ -212,13 +212,13 @@ public static String getDomainURI(String schemaName, String tableName, String na public abstract void beforeDeleteDomain(User user, Domain domain); @Override - public final void deleteDomain(User user, Domain domain) + public final void deleteDomain(User user, Domain domain, String auditUserComment) { try (DbScope.Transaction transaction = IssuesSchema.getInstance().getSchema().getScope().ensureTransaction()) { IssuesListDefService.get().deleteIssueDefsForDomain(user, domain); beforeDeleteDomain(user, domain); - domain.delete(user); + domain.delete(user, auditUserComment); transaction.commit(); } @@ -288,12 +288,12 @@ public Class getTypeClass() @Override public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable IssuesDomainKindProperties options, Container container, User user, boolean includeWarnings) + @Nullable IssuesDomainKindProperties options, Container container, User user, boolean includeWarnings, String auditUserComment) { if (options != null && StringUtils.isBlank(options.getIssueDefName())) return new ValidationException("Issue name must not be null."); - return IssuesListDefService.get().updateIssueDefinition(container, user, original, update, options); + return IssuesListDefService.get().updateIssueDefinition(container, user, original, update, options);// } @Override diff --git a/api/src/org/labkey/api/query/ExtendedTableDomainKind.java b/api/src/org/labkey/api/query/ExtendedTableDomainKind.java index d5f2d445b21..5e543c1514a 100644 --- a/api/src/org/labkey/api/query/ExtendedTableDomainKind.java +++ b/api/src/org/labkey/api/query/ExtendedTableDomainKind.java @@ -59,7 +59,7 @@ public Domain updateDomain(Container container, User user, GWTDomain gwtDomain, JSONObjec { DomainUtil.addProperty(newDomain, pd, defaultValues, propertyUris, null); } - newDomain.save(user); + newDomain.save(user);// } catch (ChangePropertyDescriptorException e) { diff --git a/api/src/org/labkey/api/reports/model/ReportPropsManager.java b/api/src/org/labkey/api/reports/model/ReportPropsManager.java index cd535d89f19..00eb6b6e17f 100644 --- a/api/src/org/labkey/api/reports/model/ReportPropsManager.java +++ b/api/src/org/labkey/api/reports/model/ReportPropsManager.java @@ -125,7 +125,7 @@ public synchronized DomainProperty ensureProperty(Container container, User user } if (dirty) - domain.save(user); + domain.save(user);// return dp; } diff --git a/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java b/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java index d6155b1dab3..0db9eefc9d6 100644 --- a/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java +++ b/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java @@ -157,10 +157,10 @@ public Domain createDomain(GWTDomain domain, SampleTypeDomainKindProperties argu @NotNull @Override - public ValidationException updateDomain(GWTDomain original, GWTDomain update, @Nullable SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings) + public ValidationException updateDomain(GWTDomain original, GWTDomain update, @Nullable SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, String auditUserComment) { JSONObject args = options != null ? options.toJSONObject() : null; - return _assayDelegate.updateDomain(original, update, args, container, user, includeWarnings); + return _assayDelegate.updateDomain(original, update, args, container, user, includeWarnings, auditUserComment); } @Override diff --git a/assay/src/org/labkey/assay/plate/AssayPlateMetadataServiceImpl.java b/assay/src/org/labkey/assay/plate/AssayPlateMetadataServiceImpl.java index a03bbd57d67..b4e7e9d619b 100644 --- a/assay/src/org/labkey/assay/plate/AssayPlateMetadataServiceImpl.java +++ b/assay/src/org/labkey/assay/plate/AssayPlateMetadataServiceImpl.java @@ -1073,7 +1073,7 @@ else if (wasValidType) { try { - replicateDomain.save(user); + replicateDomain.save(user);// } catch (ExperimentException e) { diff --git a/assay/src/org/labkey/assay/plate/PlateManager.java b/assay/src/org/labkey/assay/plate/PlateManager.java index 32a31e8d019..07660210f1b 100644 --- a/assay/src/org/labkey/assay/plate/PlateManager.java +++ b/assay/src/org/labkey/assay/plate/PlateManager.java @@ -2329,7 +2329,7 @@ public Container getPlateMetadataDomainContainer(Container container) DomainUtil.addProperty(metadataDomain, pd, new HashMap<>(), new HashSet<>(), null); } - metadataDomain.save(user); + metadataDomain.save(user);// tx.commit(); } } @@ -2376,7 +2376,7 @@ public Container getPlateMetadataDomainContainer(Container container) if (dp != null) dp.delete(); } - metadataDomain.save(user); + metadataDomain.save(user);// tx.commit(); } } diff --git a/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java b/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java index a3e461423a2..d8e47217146 100644 --- a/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java +++ b/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java @@ -129,7 +129,7 @@ public Domain createDomain(GWTDomain domain, JSONObject a String domainURI = generateDomainURI(container); Domain metadataDomain = PropertyService.get().createDomain(container, domainURI, domain.getName(), templateInfo); ensureDomainProperties(metadataDomain, container); - metadataDomain.save(user); + metadataDomain.save(user);// return PropertyService.get().getDomain(container, domainURI); } diff --git a/core/package-lock.json b/core/package-lock.json index aefecac4081..cde10079934 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -8,7 +8,7 @@ "name": "labkey-core", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.40.1", + "@labkey/components": "6.41.0-fb-domainAudit.2", "@labkey/themes": "1.4.2" }, "devDependencies": { @@ -2987,9 +2987,9 @@ } }, "node_modules/@labkey/api": { - "version": "1.40.0", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.40.0.tgz", - "integrity": "sha512-ezCVNWtLkzbH5K/CoEb69gK5q6QSgQpG9FVJse3hPR1t5Bxpt6Mt0RbmKKdOfdJcNcA30IfpcbzEOc1gz7vhZQ==" + "version": "1.40.1-fb-domainAudit.2", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.40.1-fb-domainAudit.2.tgz", + "integrity": "sha512-3/bhUCpTq04SV9pvB6f/0EJPmlWaQTUEp34wdmxjz3H6sheUPAqhAcxiV2rHwJqI3z6vE6A/ZyvXmFiXNH1XdQ==" }, "node_modules/@labkey/build": { "version": "8.5.0", @@ -3029,12 +3029,12 @@ } }, "node_modules/@labkey/components": { - "version": "6.40.1", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.40.1.tgz", - "integrity": "sha512-+8NWGOw9Mi8fxhdSlvMxCMyugHLqo1un+L/zcu7cPsRi5yjw8aaPZjao2XnJYHPSpGRfGlfWIBZgg6lDxcYepg==", + "version": "6.41.0-fb-domainAudit.2", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.41.0-fb-domainAudit.2.tgz", + "integrity": "sha512-HIR2JlYkjfDyHy+2+X3iTerdB86ZIEpX9fSMKfnwXghd82Yffz0RXga3qDa2NyrLq/lPpXK26E+krMaUDMxzIg==", "dependencies": { "@hello-pangea/dnd": "18.0.1", - "@labkey/api": "1.40.0", + "@labkey/api": "1.40.1-fb-domainAudit.2", "@testing-library/dom": "~10.4.0", "@testing-library/jest-dom": "~6.6.3", "@testing-library/react": "~16.3.0", diff --git a/core/package.json b/core/package.json index 941283d7e6a..eb3c59e83df 100644 --- a/core/package.json +++ b/core/package.json @@ -53,7 +53,7 @@ } }, "dependencies": { - "@labkey/components": "6.40.1", + "@labkey/components": "6.41.0-fb-domainAudit.2", "@labkey/themes": "1.4.2" }, "devDependencies": { diff --git a/core/src/client/DataClassDesigner/DataClassDesigner.tsx b/core/src/client/DataClassDesigner/DataClassDesigner.tsx index 91f712d2d50..4a19ca02075 100644 --- a/core/src/client/DataClassDesigner/DataClassDesigner.tsx +++ b/core/src/client/DataClassDesigner/DataClassDesigner.tsx @@ -106,7 +106,7 @@ class DataClassDesignerWrapper extends React.Component { onCancel={this.onCancel} onComplete={this.onComplete} onChange={this.onChange} - showGenIdBanner={isUpdate} + isUpdate={isUpdate} /> ) diff --git a/core/src/client/SampleTypeDesigner/SampleTypeDesigner.tsx b/core/src/client/SampleTypeDesigner/SampleTypeDesigner.tsx index 6077961f732..e3b0e084b06 100644 --- a/core/src/client/SampleTypeDesigner/SampleTypeDesigner.tsx +++ b/core/src/client/SampleTypeDesigner/SampleTypeDesigner.tsx @@ -164,7 +164,7 @@ class SampleTypeDesignerWrapper extends React.PureComponent { onChange={this.onChange} includeDataClasses={true} showLinkToStudy={true} - showGenIdBanner={isUpdate} + isUpdate={isUpdate} /> ) diff --git a/core/src/org/labkey/core/query/UsersDomainKind.java b/core/src/org/labkey/core/query/UsersDomainKind.java index 4f6664a46f2..7f2c1c7dd90 100644 --- a/core/src/org/labkey/core/query/UsersDomainKind.java +++ b/core/src/org/labkey/core/query/UsersDomainKind.java @@ -211,7 +211,7 @@ public static void ensureDomain(ModuleContext context) if (domain == null) { domain = PropertyService.get().createDomain(UsersDomainKind.getDomainContainer(), domainURI, CoreQuerySchema.USERS_TABLE_NAME); - domain.save(user); + domain.save(user);// } // ensure required fields @@ -240,7 +240,7 @@ else if (!existingProp.getName().equals(pd.getName())) } if (dirty) - domain.save(user); + domain.save(user);// transaction.commit(); } catch (Exception e) diff --git a/experiment/package-lock.json b/experiment/package-lock.json index aeba9241cd6..22c8d4df323 100644 --- a/experiment/package-lock.json +++ b/experiment/package-lock.json @@ -8,7 +8,7 @@ "name": "experiment", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.40.1" + "@labkey/components": "6.41.0-fb-domainAudit.2" }, "devDependencies": { "@labkey/build": "8.5.0", @@ -2800,9 +2800,9 @@ } }, "node_modules/@labkey/api": { - "version": "1.40.0", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.40.0.tgz", - "integrity": "sha512-ezCVNWtLkzbH5K/CoEb69gK5q6QSgQpG9FVJse3hPR1t5Bxpt6Mt0RbmKKdOfdJcNcA30IfpcbzEOc1gz7vhZQ==" + "version": "1.40.1-fb-domainAudit.2", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.40.1-fb-domainAudit.2.tgz", + "integrity": "sha512-3/bhUCpTq04SV9pvB6f/0EJPmlWaQTUEp34wdmxjz3H6sheUPAqhAcxiV2rHwJqI3z6vE6A/ZyvXmFiXNH1XdQ==" }, "node_modules/@labkey/build": { "version": "8.5.0", @@ -2842,12 +2842,12 @@ } }, "node_modules/@labkey/components": { - "version": "6.40.1", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.40.1.tgz", - "integrity": "sha512-+8NWGOw9Mi8fxhdSlvMxCMyugHLqo1un+L/zcu7cPsRi5yjw8aaPZjao2XnJYHPSpGRfGlfWIBZgg6lDxcYepg==", + "version": "6.41.0-fb-domainAudit.2", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.41.0-fb-domainAudit.2.tgz", + "integrity": "sha512-HIR2JlYkjfDyHy+2+X3iTerdB86ZIEpX9fSMKfnwXghd82Yffz0RXga3qDa2NyrLq/lPpXK26E+krMaUDMxzIg==", "dependencies": { "@hello-pangea/dnd": "18.0.1", - "@labkey/api": "1.40.0", + "@labkey/api": "1.40.1-fb-domainAudit.2", "@testing-library/dom": "~10.4.0", "@testing-library/jest-dom": "~6.6.3", "@testing-library/react": "~16.3.0", diff --git a/experiment/package.json b/experiment/package.json index 370becb13ca..3b38905770c 100644 --- a/experiment/package.json +++ b/experiment/package.json @@ -13,7 +13,7 @@ "test-integration": "cross-env NODE_ENV=test jest --ci --runInBand -c test/js/jest.config.integration.js" }, "dependencies": { - "@labkey/components": "6.40.1" + "@labkey/components": "6.41.0-fb-domainAudit.2" }, "devDependencies": { "@labkey/build": "8.5.0", diff --git a/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java b/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java index 3b2871877a3..d9cba286ab3 100644 --- a/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java +++ b/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java @@ -402,23 +402,24 @@ public Domain createDomain(GWTDomain domain, DataClassDom @Nullable DataClassDomainKindProperties options, Container container, User user, - boolean includeWarnings + boolean includeWarnings, + String auditUserComment ) { ExpDataClass dc = ExperimentService.get().getDataClass(original.getDomainURI()); if (dc == null) return new ValidationException(String.format("Could not resolve data class from LSID \"%s\".", original.getDomainURI())); - return ExperimentService.get().updateDataClass(container, user, dc, options, original, update); + return ExperimentService.get().updateDataClass(container, user, dc, options, original, update, auditUserComment); } @Override - public void deleteDomain(User user, Domain domain) + public void deleteDomain(User user, Domain domain, String auditUserComment) { ExpDataClass dc = getDataClass(domain); if (dc == null) throw new NotFoundException("DataClass not found: " + domain.getTypeURI()); - dc.delete(user); + dc.delete(user, auditUserComment); } @Override diff --git a/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java b/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java index bf9a3413255..c37ee3f5ca2 100644 --- a/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java @@ -269,7 +269,7 @@ public void save(User user) domain = PropertyService.get().createDomain(getContainer(), getLSID(), getName()); try { - domain.save(user); + domain.save(user);// } catch (ChangePropertyDescriptorException e) { diff --git a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java index 7007f2e7433..19abc38c4a4 100644 --- a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java @@ -760,7 +760,7 @@ public Domain getDomain() _domain = PropertyService.get().createDomain(getContainer(), getLSID(), getName()); try { - _domain.save(null); + _domain.save(null);// } catch (ChangePropertyDescriptorException e) { @@ -846,7 +846,7 @@ public void save(User user) domain = PropertyService.get().createDomain(getContainer(), getLSID(), getName()); try { - domain.save(user); + domain.save(user);// } catch (ChangePropertyDescriptorException e) { diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index ebe7f0a04e0..c53ebf950c0 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -7939,7 +7939,7 @@ else if (domain.getPropertyByName(propertyName) != null) // issue 25275 if (kind != null) domain.setPropertyForeignKeys(kind.getPropertyForeignKeys(c)); - domain.save(u); + domain.save(u);// impl.save(u); SchemaKey schemaKey = SchemaKey.fromParts(ExpSchema.SCHEMA_NAME, DataClassUserSchema.NAME); @@ -7969,7 +7969,8 @@ else if (domain.getPropertyByName(propertyName) != null) // issue 25275 public ValidationException updateDataClass(@NotNull Container c, @NotNull User u, @NotNull ExpDataClass dataClass, @Nullable DataClassDomainKindProperties properties, GWTDomain original, - GWTDomain update) + GWTDomain update, + String auditUserComment) { ValidationException errors; @@ -8026,7 +8027,7 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u auditComment = "The name of the data class '" + oldDataClassName + "' was changed to '" + newName + "'."; } - errors = DomainUtil.updateDomainDescriptor(original, update, c, u, hasNameChange, auditComment); + errors = DomainUtil.updateDomainDescriptor(original, update, c, u, hasNameChange, auditComment, auditUserComment);// QueryService.get().saveCalculatedFieldsMetadata(schemaKey.toString(), update.getQueryName(), hasNameChange ? newName : null, update.getCalculatedFields(), !original.getCalculatedFields().isEmpty(), u, c); diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index dcac2532ac9..3dc4563a88d 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -605,7 +605,7 @@ public void deleteSampleType(int rowId, Container c, User user, @Nullable String } Domain d = source.getDomain(); - d.delete(user); + d.delete(user, auditUserComment); ExperimentServiceImpl.get().deleteDomainObjects(source.getContainer(), source.getLSID()); @@ -880,7 +880,7 @@ public ExpSampleTypeImpl createSampleType(Container c, User u, String name, Stri { try { - domain.save(u); + domain.save(u);// st.save(u); QueryService.get().saveCalculatedFieldsMetadata(SamplesSchema.SCHEMA_NAME, name, null, calculatedFields, false, u, c); DefaultValueService.get().setDefaultValues(domain.getContainer(), defaultValues); @@ -999,7 +999,7 @@ private void validateSampleTypeName(Container container, User user, String name, } @Override - public ValidationException updateSampleType(GWTDomain original, GWTDomain update, SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings) + public ValidationException updateSampleType(GWTDomain original, GWTDomain update, SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, String auditUserComment) { ValidationException errors; @@ -1086,7 +1086,7 @@ public ValidationException updateSampleType(GWTDomain { if (isDomainNew) - addAuditEvent(user, extraAuditComment + String.format("The domain %s was created", _dd.getName()), null); + addAuditEvent(user, extraAuditComment + String.format("The domain %s was created", _dd.getName()), auditUserComment); if (finalPropChanged) { - final Long domainEventId = addAuditEvent(user, extraAuditComment + String.format("The column(s) of domain %s were modified", _dd.getName()), null); + final Long domainEventId = addAuditEvent(user, extraAuditComment + String.format("The column(s) of domain %s were modified", _dd.getName()), auditUserComment); propertyAuditInfo.forEach(auditInfo -> addPropertyAuditEvent(user, auditInfo.getProp(), auditInfo.getAction(), domainEventId, getName(), auditInfo.getDetails())); } else if (!isDomainNew) { - addAuditEvent(user, extraAuditComment + String.format("The descriptor of domain %s was updated", _dd.getName()), null); + addAuditEvent(user, extraAuditComment + String.format("The descriptor of domain %s was updated", _dd.getName()), auditUserComment); } }; transaction.addCommitTask(afterDomainCommit, DbScope.CommitTaskOption.POSTCOMMIT); diff --git a/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java b/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java index 4727b009386..0daddef5677 100644 --- a/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java +++ b/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java @@ -201,7 +201,7 @@ public ModelAndView getView(DomainForm form, BindException errors) // save the domain so that we ensure it exists before we try to edit it try (var ignored = SpringActionController.ignoreSqlUpdates()) { - _domain.save(getUser()); + _domain.save(getUser());// } catch (ChangePropertyDescriptorException e) { @@ -543,7 +543,7 @@ public Object execute(DomainApiForm form, BindException errors) boolean includeWarnings = form.includeWarnings(); boolean hasErrors = false; - ValidationException updateErrors = updateDomain(originalDomain, newDomain, form.getOptions(), getContainer(), getUser(), includeWarnings); + ValidationException updateErrors = updateDomain(originalDomain, newDomain, form.getOptions(), getContainer(), getUser(), includeWarnings, form.getAuditUserComment()); for (ValidationError ve : updateErrors.getErrors()) { @@ -760,7 +760,7 @@ public Object execute(UpdateDomainApiForm form, BindException errors) throws Exc boolean includeWarnings = form.isIncludeWarnings(); boolean hasErrors = false; - ValidationException updateErrors = updateDomain(originalDomain, newDomain, null, getContainer(), getUser(), includeWarnings); + ValidationException updateErrors = updateDomain(originalDomain, newDomain, null, getContainer(), getUser(), includeWarnings, null); for (ValidationError ve : updateErrors.getErrors()) { if (ve.getSeverity().equals(ValidationException.SEVERITY.ERROR)) @@ -809,7 +809,7 @@ public Object execute(DomainApiForm form, BindException errors) String queryName = form.getQueryName(); String schemaName = form.getSchemaName(); - deleteDomain(schemaName, queryName, getContainer(), getUser()); + deleteDomain(schemaName, queryName, getContainer(), getUser(), form.getAuditUserComment()); return success("Domain deleted"); } } @@ -832,6 +832,7 @@ public static class DomainApiForm private String queryName; private Integer domainId; private boolean includeWarnings; + private String auditUserComment; public Integer getDomainId() { @@ -1001,6 +1002,16 @@ public void setIncludeWarnings(boolean includeWarnings) @JsonIgnore private Map optionsProperties; + public String getAuditUserComment() + { + return auditUserComment; + } + + public void setAuditUserComment(String auditUserComment) + { + this.auditUserComment = auditUserComment; + } + /** * Convenience method to cache options map */ @@ -1477,7 +1488,7 @@ public void setTsvText(String tsvText) /** @return Errors encountered during the save attempt */ @NotNull private static ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable JSONObject options, Container container, User user, boolean includeWarnings) + @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) { DomainKind kind = PropertyService.get().getDomainKind(original.getDomainURI()); if (kind == null) @@ -1498,7 +1509,7 @@ private static ValidationException updateDomain(GWTDomain original, GWTDomain update, - ListDomainKindProperties listProperties, Container container, User user, boolean includeWarnings) + ListDomainKindProperties listProperties, Container container, User user, boolean includeWarnings, String auditUserComment) { ValidationException exception; @@ -576,7 +576,7 @@ else if (update.getDescription() != null) } //update domain properties - exception.addErrors(DomainUtil.updateDomainDescriptor(original, update, container, user, hasNameChange, auditComment)); + exception.addErrors(DomainUtil.updateDomainDescriptor(original, update, container, user, hasNameChange, auditComment, auditUserComment)); QueryService.get().saveCalculatedFieldsMetadata(ListQuerySchema.NAME, update.getQueryName(), hasNameChange ? update.getName() : null, update.getCalculatedFields(), !original.getCalculatedFields().isEmpty(), user, container); } @@ -681,7 +681,7 @@ private GWTPropertyDescriptor findField(int id, List domain, JSONObject a } @Override - public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, @Nullable JSONObject options, Container container, User user, boolean includeWarnings) + public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) { ValidationException validation = checkFieldNameLength(update); if (validation != null) return validation; - return super.updateDomain(original, update, options, container, user, includeWarnings); + return super.updateDomain(original, update, options, container, user, includeWarnings, auditUserComment); } // Issue 52666: Don't allow property names that might cause problems due to a storage vs user-facing name diff --git a/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java b/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java index b09617077f2..866bb2d03e9 100644 --- a/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java +++ b/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java @@ -168,7 +168,7 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) @Override public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable JSONObject options, Container container, User user, boolean includeWarnings) + @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) { ValidationException exception; try (var transaction = SpecimenSchema.get().getScope().ensureTransaction()) @@ -213,7 +213,7 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) } exception = checkRollups(null, optionalSpecimenProps, container, user, exception, includeWarnings); - exception.addErrors(super.updateDomain(original, update, options, container, user, includeWarnings)); + exception.addErrors(super.updateDomain(original, update, options, container, user, includeWarnings, auditUserComment)); if (!exception.hasErrors()) { diff --git a/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java b/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java index d6245e5e7f7..058fd62ba91 100644 --- a/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java +++ b/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java @@ -233,7 +233,7 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) @Override public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable JSONObject options, Container container, User user, boolean includeWarnings) + @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) { ValidationException validationException; try (var transaction = SpecimenSchema.get().getScope().ensureTransaction()) @@ -253,7 +253,7 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) } } - validationException.addErrors(super.updateDomain(original, update, options, container, user, includeWarnings)); + validationException.addErrors(super.updateDomain(original, update, options, container, user, includeWarnings, auditUserComment)); if (!validationException.hasErrors()) { diff --git a/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java b/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java index 868dad573d8..fafbce9bd0a 100644 --- a/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java +++ b/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java @@ -111,7 +111,7 @@ public final Domain getDomain(String tableName, boolean create) } domain.setPropertyForeignKeys(domainKind.getPropertyForeignKeys(_container, SpecimenTablesProvider.this)); - domain.save(_user); + domain.save(_user);// // Refresh the domain. save() doesn't populate provisioned schema and table names, e.g. return PropertyService.get().getDomain(_container, domainURI); diff --git a/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java b/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java index 9bfbe03b7ec..2ee468eca9f 100644 --- a/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java +++ b/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java @@ -171,7 +171,8 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) @Override public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable JSONObject options, Container container, User user, boolean includeWarnings){ + @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) + { ValidationException exception; try (var transaction = SpecimenSchema.get().getScope().ensureTransaction()) { @@ -216,7 +217,7 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) } exception = checkRollups(optionalVialFields, null, container, user, exception, includeWarnings); - exception.addErrors(super.updateDomain(original, update, options, container, user, includeWarnings)); + exception.addErrors(super.updateDomain(original, update, options, container, user, includeWarnings, auditUserComment)); if (!exception.hasErrors()) { diff --git a/study/src/org/labkey/study/assay/StudyPublishManager.java b/study/src/org/labkey/study/assay/StudyPublishManager.java index 41818beb073..f34bba47fd1 100644 --- a/study/src/org/labkey/study/assay/StudyPublishManager.java +++ b/study/src/org/labkey/study/assay/StudyPublishManager.java @@ -725,7 +725,7 @@ private Map ensurePropertyDescriptors( if (domain == null) { domain = PropertyService.get().createDomain(dataset.getContainer(), dataset.getTypeURI(), dataset.getName()); - domain.save(user); + domain.save(user);// } // Strip out any spaces from existing PropertyDescriptors in the dataset boolean propertyChanged = false; @@ -740,7 +740,7 @@ private Map ensurePropertyDescriptors( } if (propertyChanged) { - domain.save(user); + domain.save(user);// } // Strip out spaces from any proposed PropertyDescriptor names @@ -862,7 +862,7 @@ private boolean renameRunPropertyToBatch(Domain domain, Map prop property.setPropertyURI(property.getPropertyURI().replace("#Run", "#Batch")); propertyNamesToUris.remove(oldName); propertyNamesToUris.put(newPdName, property.getPropertyURI()); - domain.save(user); + domain.save(user);// return true; } } diff --git a/study/src/org/labkey/study/controllers/StudyController.java b/study/src/org/labkey/study/controllers/StudyController.java index 8bdc89d3e89..f49cf1fce72 100644 --- a/study/src/org/labkey/study/controllers/StudyController.java +++ b/study/src/org/labkey/study/controllers/StudyController.java @@ -5152,7 +5152,7 @@ private Dataset createDataset(StudySnapshotForm form, BindException errors) thro { DatasetSnapshotProvider.addAsDomainProperty(d, col); } - d.save(getUser()); + d.save(getUser());// return def; } diff --git a/study/src/org/labkey/study/controllers/StudyDefinitionController.java b/study/src/org/labkey/study/controllers/StudyDefinitionController.java index 27f4b31f5e7..3cc29d2fc79 100644 --- a/study/src/org/labkey/study/controllers/StudyDefinitionController.java +++ b/study/src/org/labkey/study/controllers/StudyDefinitionController.java @@ -74,7 +74,7 @@ public boolean handlePost(ReturnUrlForm form, BindException errors) throws Excep if (_domain == null) { _domain = PropertyService.get().createDomain(getContainer(), domainURI, domainInfo.getDomainName()); - _domain.save(getUser()); + _domain.save(getUser());// } return true; diff --git a/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java b/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java index 24723507f4a..99ee1178d34 100644 --- a/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java +++ b/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java @@ -575,7 +575,7 @@ private void updateDomainProperties(User user, QuerySnapshotDefinition snapshotD } } if (dirty) - snapshotDomain.save(user); + snapshotDomain.save(user);// } } } diff --git a/study/src/org/labkey/study/model/DatasetDomainKind.java b/study/src/org/labkey/study/model/DatasetDomainKind.java index ee63af411c6..acd5c34ebb0 100644 --- a/study/src/org/labkey/study/model/DatasetDomainKind.java +++ b/study/src/org/labkey/study/model/DatasetDomainKind.java @@ -715,7 +715,7 @@ private ValidationException updateDataset(DatasetDomainKindProperties datasetPro @Override public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, - DatasetDomainKindProperties datasetProperties, Container container, User user, boolean includeWarnings) + DatasetDomainKindProperties datasetProperties, Container container, User user, boolean includeWarnings, String userComment) { assert original.getDomainURI().equals(update.getDomainURI()); StudyImpl study = StudyManager.getInstance().getStudy(container); @@ -760,7 +760,7 @@ private ValidationException updateDataset(DatasetDomainKindProperties datasetPro } @Override - public void deleteDomain(User user, Domain domain) + public void deleteDomain(User user, Domain domain, String userComment)// { DatasetDefinition def = StudyManager.getInstance().getDatasetDefinition(domain.getTypeURI()); if (def == null) From 11b524331790b47e6920c9de80bd2d0111e0bc69 Mon Sep 17 00:00:00 2001 From: XingY Date: Sun, 11 May 2025 22:07:39 -0700 Subject: [PATCH 02/30] Add detailed auditing of domain changes & comment ability --- .../api/assay/AbstractAssayProvider.java | 10 ++- .../org/labkey/api/assay/AssayProvider.java | 2 +- .../labkey/api/data/ConditionalFormat.java | 24 +++++++ .../api/dataiterator/SimpleTranslator.java | 2 +- .../org/labkey/api/exp/api/ExpDataClass.java | 2 + .../api/exp/api/ExperimentJSONConverter.java | 17 +++++ .../api/exp/property/DomainProperty.java | 3 + .../labkey/api/exp/property/DomainUtil.java | 67 +++++++++++++++++-- .../labkey/assay/AssayDomainServiceImpl.java | 35 +++++++--- core/package-lock.json | 8 +-- core/package.json | 2 +- experiment/package-lock.json | 8 +-- experiment/package.json | 2 +- .../experiment/ExpDataFileListener.java | 2 +- .../experiment/api/ExpDataClassImpl.java | 1 + .../experiment/api/ExperimentServiceImpl.java | 23 +++++-- .../experiment/api/SampleTypeServiceImpl.java | 28 ++++++-- .../experiment/api/property/DomainImpl.java | 43 ++++++------ .../api/property/DomainPropertyImpl.java | 27 ++++++++ .../api/property/PropertyValidator.java | 8 +++ .../org/labkey/list/model/ListDomainKind.java | 45 ++++++++++--- .../query/controllers/QueryController.java | 12 ++++ .../labkey/study/model/DatasetDomainKind.java | 27 ++++++-- 23 files changed, 322 insertions(+), 76 deletions(-) diff --git a/api/src/org/labkey/api/assay/AbstractAssayProvider.java b/api/src/org/labkey/api/assay/AbstractAssayProvider.java index 73943f8bd2b..de6343ecbe1 100644 --- a/api/src/org/labkey/api/assay/AbstractAssayProvider.java +++ b/api/src/org/labkey/api/assay/AbstractAssayProvider.java @@ -74,6 +74,7 @@ import org.labkey.api.exp.api.IAssayDomainType; import org.labkey.api.exp.property.Domain; import org.labkey.api.exp.property.DomainProperty; +import org.labkey.api.exp.property.DomainUtil; import org.labkey.api.exp.property.PropertyService; import org.labkey.api.exp.query.ExpRunTable; import org.labkey.api.files.FileContentService; @@ -1246,7 +1247,7 @@ public boolean hasUsefulDetailsPage() private static final String SCRIPT_PATH_DELIMITER = "|"; @Override - public ValidationException setValidationAndAnalysisScripts(ExpProtocol protocol, @NotNull List scripts) throws ExperimentException + public Pair setValidationAndAnalysisScripts(ExpProtocol protocol, @NotNull List scripts) throws ExperimentException { Map props = new HashMap<>(protocol.getObjectProperties()); String propertyURI = ScriptType.TRANSFORM.getPropertyURI(protocol); @@ -1290,9 +1291,12 @@ public ValidationException setValidationAndAnalysisScripts(ExpProtocol protocol, // don't persist if any validation error has severity=ERROR if (validationErrors.getErrors().stream().anyMatch(e -> SEVERITY.ERROR == e.getSeverity())) - return validationErrors; + return new Pair<>(validationErrors, null); JSONArray json = AnalysisScript.toJson(scripts); + ObjectProperty oldProp = props.get(propertyURI); + String oldJson = oldProp == null ? null : oldProp.getStringValue(); + String auditMsg = DomainUtil.getPropChangeMsg("TransformScript", oldJson, json); if (json != null) { ObjectProperty prop = new ObjectProperty(protocol.getLSID(), protocol.getContainer(), @@ -1305,7 +1309,7 @@ public ValidationException setValidationAndAnalysisScripts(ExpProtocol protocol, } protocol.setObjectProperties(props); - return validationErrors; + return new Pair<>(validationErrors, auditMsg); } /** For migrating legacy assay designs that have separate transform and validation script properties */ diff --git a/api/src/org/labkey/api/assay/AssayProvider.java b/api/src/org/labkey/api/assay/AssayProvider.java index b5e972f26b8..4546eb6bc08 100644 --- a/api/src/org/labkey/api/assay/AssayProvider.java +++ b/api/src/org/labkey/api/assay/AssayProvider.java @@ -240,7 +240,7 @@ enum Scope * File based QC and analysis scripts can be added to a protocol and invoked when the validate * method is called. Set to an empty list if no scripts exist. */ - ValidationException setValidationAndAnalysisScripts(ExpProtocol protocol, @NotNull List scripts) throws ExperimentException; + Pair setValidationAndAnalysisScripts(ExpProtocol protocol, @NotNull List scripts) throws ExperimentException; @NotNull List getValidationAndAnalysisScripts(ExpProtocol protocol, Scope scope); diff --git a/api/src/org/labkey/api/data/ConditionalFormat.java b/api/src/org/labkey/api/data/ConditionalFormat.java index d838b7c13bc..b7623a49f6a 100644 --- a/api/src/org/labkey/api/data/ConditionalFormat.java +++ b/api/src/org/labkey/api/data/ConditionalFormat.java @@ -15,6 +15,7 @@ */ package org.labkey.api.data; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.fhcrc.cpas.exp.xml.PropertyDescriptorType; @@ -376,4 +377,27 @@ private static boolean addToXML(List formats, Co return success; } + + public String toStringVal() + { + return getFilter() + ": " + getCssStyle(); + } + + public static String toStringVal(List formats) + { + if (formats == null || formats.isEmpty()) + return ""; + + List strings = new ArrayList<>(); + formats.forEach(format -> { + if (format instanceof ConditionalFormat c) + strings.add(c.toStringVal()); + else + { + ConditionalFormat conditionalFormat = new ConditionalFormat(format); + strings.add(conditionalFormat.toStringVal()); + } + }); + return StringUtils.join(strings, ", "); + } } diff --git a/api/src/org/labkey/api/dataiterator/SimpleTranslator.java b/api/src/org/labkey/api/dataiterator/SimpleTranslator.java index 858c93bd955..4ea042f55d9 100644 --- a/api/src/org/labkey/api/dataiterator/SimpleTranslator.java +++ b/api/src/org/labkey/api/dataiterator/SimpleTranslator.java @@ -455,7 +455,7 @@ private Object getSingleValue(Object k, Collection vs) if (vs.size() == 1) return vs.iterator().next(); - throw new ConversionException("Found " + vs.size() + " values matching: " + String.valueOf(k)); + throw new ConversionExceptionWithMessage("Found " + vs.size() + " values matching: " + String.valueOf(k)); } } diff --git a/api/src/org/labkey/api/exp/api/ExpDataClass.java b/api/src/org/labkey/api/exp/api/ExpDataClass.java index 49ff8842e22..fa7aeb8b6d1 100644 --- a/api/src/org/labkey/api/exp/api/ExpDataClass.java +++ b/api/src/org/labkey/api/exp/api/ExpDataClass.java @@ -118,5 +118,7 @@ public interface ExpDataClass extends ExpObject void setImportAliasMap(Map> aliasMap); + String getImportAliasJson(); + boolean hasData(); } diff --git a/api/src/org/labkey/api/exp/api/ExperimentJSONConverter.java b/api/src/org/labkey/api/exp/api/ExperimentJSONConverter.java index 36ca5e86bd6..cf2a6450fba 100644 --- a/api/src/org/labkey/api/exp/api/ExperimentJSONConverter.java +++ b/api/src/org/labkey/api/exp/api/ExperimentJSONConverter.java @@ -53,6 +53,7 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -61,6 +62,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.TreeMap; import java.util.stream.Collectors; import static org.labkey.api.exp.api.ExpData.DATA_INPUTS_PREFIX; @@ -957,6 +959,21 @@ else if (value instanceof Map map) return aliases; } + public static @Nullable String getImportAliasStringVal(@Nullable Map> importAliases) + { + if (importAliases == null || importAliases.isEmpty()) + return null; + + List aliasStrings = new ArrayList<>(); + importAliases.forEach((key, value) -> { + String val = (String) value.get("inputType"); + String requiredStr = Objects.toString(value.get("required"), "false"); + aliasStrings.add(key + "(" + val + ",required=" + requiredStr + ")"); + }); + Collections.sort(aliasStrings); + return StringUtils.join(aliasStrings, "; "); + } + public static final class TestCase extends Assert { @Test diff --git a/api/src/org/labkey/api/exp/property/DomainProperty.java b/api/src/org/labkey/api/exp/property/DomainProperty.java index 267683f99b7..633560b1644 100644 --- a/api/src/org/labkey/api/exp/property/DomainProperty.java +++ b/api/src/org/labkey/api/exp/property/DomainProperty.java @@ -156,4 +156,7 @@ default boolean isUniqueIdField() boolean isScannable(); void setScannable(boolean scannable); + + String getPropertyValidatorStringVal(); + void checkValidatorEdit(String oldValidatorStr, PropertyDescriptor oldPropertyDescriptor); } diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index 3de5b1359ef..7cec362abe3 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -750,6 +750,51 @@ public static ValidationException updateDomainDescriptor(GWTDomain " + " ;"; + if (oldProp == null) + return propertyName + ": -> " + newProp + ";"; + + if (oldProp.equals(newProp)) + return ""; + + return propertyName + ": " + oldProp + " -> " + newProp + ";"; + } + + public static String getPropChangeMsg(String propertyName, Object oldProp, Object newProp) + { + if (oldProp == newProp) + return ""; + + if (oldProp != null && newProp == null) + return getStringPropChangeMsg(propertyName, oldProp.toString(), null); + if (oldProp == null) + return getStringPropChangeMsg(propertyName, null, newProp.toString()); + + return getStringPropChangeMsg(propertyName, oldProp.toString(), newProp.toString()); + } + + public static String getCollectionPropChangeMsg(String propertyName, Collection oldProp, Collection newProp) + { + if (oldProp == newProp) + return ""; + + if (oldProp != null && newProp == null) + return getPropChangeMsg(propertyName, StringUtils.join(oldProp), null); + if (oldProp == null) + return getPropChangeMsg(propertyName, null, StringUtils.join(newProp)); + + return getPropChangeMsg(propertyName, StringUtils.join(oldProp), StringUtils.join(newProp)); + } + /** @return Errors encountered during the save attempt */ @NotNull public static ValidationException updateDomainDescriptor(GWTDomain orig, GWTDomain update, Container container, User user, boolean updateDomainName, @Nullable String auditComment, @Nullable String auditUserComment) @@ -779,9 +824,11 @@ public static ValidationException updateDomainDescriptor(GWTDomain disabledSystemFieldsUpdate = kind.getDisabledSystemFields(update.getDisabledSystemFields()); + changeDetails.append(getCollectionPropChangeMsg("DisabledSystemFields",d.getDisabledSystemFields(), disabledSystemFieldsUpdate)); + d.setDisabledSystemFields(disabledSystemFieldsUpdate); // NOTE that DomainImpl.save() does an optimistic concurrency check, but we still need to check here. // This code is diff'ing two GWTDomains and applying those changes to Domain d. We need to make sure we're @@ -875,7 +924,7 @@ public static ValidationException updateDomainDescriptor(GWTDomain> propTextChoiceValueUpdates = updatePropertyValidators(p, old, pd); + List> propTextChoiceValueUpdates = updatePropertyValidators(p, old, pd); // if (propTextChoiceValueUpdates != null) textChoiceValueUpdates.put(p, propTextChoiceValueUpdates); if (old.equals(pd)) @@ -914,7 +963,9 @@ public static ValidationException updateDomainDescriptor(GWTDomain(defaultValues); try @@ -1191,11 +1242,13 @@ private static void _copyProperties(DomainProperty to, GWTPropertyDescriptor fro to.setDerivationDataScope(from.getDerivationDataScope()); } - private static List> updatePropertyValidators(DomainProperty dp, GWTPropertyDescriptor oldPd, GWTPropertyDescriptor newPd) + private static List> updatePropertyValidators(DomainProperty dp, @Nullable GWTPropertyDescriptor oldPd, GWTPropertyDescriptor newPd) { Map newProps = new HashMap<>(); List> valueUpdates = new ArrayList<>(); + PropertyDescriptor oldPropertyDescriptor = dp.getPropertyDescriptor().clone(); + String oldValidatorStr = dp.getPropertyValidatorStringVal(); // record the old value before dp is mutated for (GWTPropertyValidator v : newPd.getPropertyValidators()) { if (v.getRowId() != 0) @@ -1235,11 +1288,11 @@ else if (prop == null) // deal with removed validators for (GWTPropertyValidator gpv : deleted) dp.removeValidator(gpv.getRowId()); - - return valueUpdates; } - return null; + dp.checkValidatorEdit(oldValidatorStr, oldPropertyDescriptor); // mark dirty as needed + + return oldPd != null ? valueUpdates : null; } private static void updateTextChoiceValueRows(Domain domain, User user, String propName, Map valueUpdates, ValidationException errors) diff --git a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java index ebd0d26da75..9325fbbffcf 100644 --- a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java +++ b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java @@ -416,6 +416,7 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr if (assay.getAutoLinkCategory() != null && assay.getAutoLinkCategory().length() > 200) throw new ValidationException("Linked Dataset Category name must be shorter than 200 characters."); + StringBuilder changeDetails = new StringBuilder(); ExpProtocol protocol; boolean isNew = assay.getProtocolId() == null; boolean hasNameChange = false; @@ -451,7 +452,7 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr else { GWTDomain previous = DomainUtil.getDomainDescriptor(getUser(), domain.getDomainURI(), protocol.getContainer()); - updateDomainDescriptor(assayProvider, protocol, previous, domain, false); + updateDomainDescriptor(assayProvider, protocol, previous, domain, false, null); domainURIs.add(domain.getDomainURI()); } } @@ -475,12 +476,15 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr hasNameChange = !assay.getName().equals(oldAssayName); if (hasNameChange) { + changeDetails.append("The name of the assay domain '" + oldAssayName + "' was changed to '" + assay.getName() + "'."); String nameError = DomainUtil.validateDomainName(assay.getName(), "Assay Design", false); if (nameError != null) throw new ValidationException(nameError); } protocol.setName(assay.getName()); + changeDetails.append(DomainUtil.getStringPropChangeMsg("Description", protocol.getProtocolDescription(), assay.getDescription())); protocol.setProtocolDescription(assay.getDescription()); + changeDetails.append(DomainUtil.getStringPropChangeMsg("Status", protocol.getStatus().toString(), assay.getStatus())); if (assay.getStatus() != null) protocol.setStatus(ExpProtocol.Status.valueOf(assay.getStatus())); } @@ -556,10 +560,16 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr if (detectionMethod == null) throw new ValidationException("The selected detection method could not be found."); + String oldDetectionMethod = dmProvider.getSelectedDetectionMethod(getContainer(), protocol); + changeDetails.append(DomainUtil.getPropChangeMsg("DetectionMethod", oldDetectionMethod, detectionMethod)); dmProvider.setSelectedDetectionMethod(getContainer(), protocol, detectionMethod); } - ValidationException scriptValidation = provider.setValidationAndAnalysisScripts(protocol, transformScripts); + Pair scriptValidationResult = provider.setValidationAndAnalysisScripts(protocol, transformScripts); + ValidationException scriptValidation = scriptValidationResult.first; + String transformScriptChangeMsg = scriptValidationResult.second; + if (transformScriptChangeMsg != null) + changeDetails.append(transformScriptChangeMsg); if (scriptValidation.hasErrors()) { for (var error : scriptValidation.getErrors()) @@ -574,11 +584,17 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr } } + changeDetails.append(DomainUtil.getPropChangeMsg("SaveScriptFiles", provider.isSaveScriptFiles(protocol), assay.isSaveScriptFiles())); provider.setSaveScriptFiles(protocol, assay.isSaveScriptFiles()); + changeDetails.append(DomainUtil.getPropChangeMsg("IsEditableResults", provider.isEditableResults(protocol), assay.isEditableResults())); provider.setEditableResults(protocol, assay.isEditableResults()); + changeDetails.append(DomainUtil.getPropChangeMsg("IsEditableRuns", provider.isEditableRuns(protocol), assay.isEditableRuns())); provider.setEditableRuns(protocol, assay.isEditableRuns()); + changeDetails.append(DomainUtil.getPropChangeMsg("IsBackgroundUpload", provider.isBackgroundUpload(protocol), assay.isBackgroundUpload())); provider.setBackgroundUpload(protocol, assay.isBackgroundUpload()); + changeDetails.append(DomainUtil.getPropChangeMsg("IsQcEnabled", provider.isQCEnabled(protocol), assay.isQcEnabled())); provider.setQCEnabled(protocol, assay.isQcEnabled()); + changeDetails.append(DomainUtil.getPropChangeMsg("IsPlateMetadataEnabled", provider.isPlateMetadataEnabled(protocol), assay.isPlateMetadata())); provider.setPlateMetadataEnabled(protocol, assay.isPlateMetadata()); Map props = new HashMap<>(protocol.getObjectProperties()); @@ -590,12 +606,18 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr throw new ValidationException("No such auto-link target container id: " + autoLinkTargetContainerId); } + ObjectProperty oldAutoLinkTargetContainer = props.get(StudyPublishService.AUTO_LINK_TARGET_PROPERTY_URI); + String oldAutoLinkTargetContainerId = oldAutoLinkTargetContainer == null ? null : oldAutoLinkTargetContainer.getStringValue(); + changeDetails.append(DomainUtil.getPropChangeMsg("AutoCopyTargetContainer", oldAutoLinkTargetContainerId, autoLinkTargetContainerId)); if (autoLinkTargetContainerId != null) props.put(StudyPublishService.AUTO_LINK_TARGET_PROPERTY_URI, new ObjectProperty(protocol.getLSID(), protocol.getContainer(), StudyPublishService.AUTO_LINK_TARGET_PROPERTY_URI, autoLinkTargetContainerId)); else props.remove(StudyPublishService.AUTO_LINK_TARGET_PROPERTY_URI); String autoLinkCategory = assay.getAutoLinkCategory(); + ObjectProperty oldAutoLinkCategory = props.get(StudyPublishService.AUTO_LINK_CATEGORY_PROPERTY_URI); + String oldAutoLinkCategoryValue = oldAutoLinkCategory == null ? null : oldAutoLinkCategory.getStringValue(); + changeDetails.append(DomainUtil.getPropChangeMsg("AutoLinkCategory", oldAutoLinkCategoryValue, autoLinkCategory)); if (autoLinkCategory != null) props.put(StudyPublishService.AUTO_LINK_CATEGORY_PROPERTY_URI, new ObjectProperty(protocol.getLSID(), protocol.getContainer(), StudyPublishService.AUTO_LINK_CATEGORY_PROPERTY_URI, autoLinkCategory)); else @@ -608,7 +630,7 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr for (GWTDomain domain : assay.getDomains()) { GWTDomain previous = DomainUtil.getDomainDescriptor(getUser(), domain.getDomainURI(), protocol.getContainer()); - updateDomainDescriptor(provider, protocol, previous, domain, hasNameChange); + updateDomainDescriptor(provider, protocol, previous, domain, hasNameChange, changeDetails.toString()); boolean hasExistingCalcFields = previous != null && !previous.getCalculatedFields().isEmpty(); GWTDomain savedDomain = DomainUtil.getDomainDescriptor(getUser(), domain.getDomainURI(), protocol.getContainer()); @@ -642,7 +664,8 @@ private void updateDomainDescriptor( ExpProtocol protocol, GWTDomain original, GWTDomain update, - boolean hasNameChange + boolean hasNameChange, + String auditComment ) throws ValidationException { for (GWTPropertyDescriptor prop : update.getFields()) @@ -651,10 +674,6 @@ private void updateDomainDescriptor( prop.setLookupQuery(prop.getLookupQuery().replace(AbstractAssayProvider.ASSAY_NAME_SUBSTITUTION, protocol.getName())); } - String auditComment = null; - if (hasNameChange) - auditComment = "The name of the assay domain '" + original.getName() + "' was changed to '" + update.getName() + "'."; - // Before update provider.beforeDomainChange(getUser(), protocol, original, update); diff --git a/core/package-lock.json b/core/package-lock.json index cde10079934..f83733a37ce 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -8,7 +8,7 @@ "name": "labkey-core", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.41.0-fb-domainAudit.2", + "@labkey/components": "6.41.0-fb-domainAudit.3", "@labkey/themes": "1.4.2" }, "devDependencies": { @@ -3029,9 +3029,9 @@ } }, "node_modules/@labkey/components": { - "version": "6.41.0-fb-domainAudit.2", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.41.0-fb-domainAudit.2.tgz", - "integrity": "sha512-HIR2JlYkjfDyHy+2+X3iTerdB86ZIEpX9fSMKfnwXghd82Yffz0RXga3qDa2NyrLq/lPpXK26E+krMaUDMxzIg==", + "version": "6.41.0-fb-domainAudit.3", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.41.0-fb-domainAudit.3.tgz", + "integrity": "sha512-vEWzebLZhHUJlvp70vDVW894w8YLc+rA7CqmqVWtXV+gQLnh+bYsDFmwHJF6nrugMUFQs/Clk4sVIQ5YpOrLng==", "dependencies": { "@hello-pangea/dnd": "18.0.1", "@labkey/api": "1.40.1-fb-domainAudit.2", diff --git a/core/package.json b/core/package.json index eb3c59e83df..ba26773cab9 100644 --- a/core/package.json +++ b/core/package.json @@ -53,7 +53,7 @@ } }, "dependencies": { - "@labkey/components": "6.41.0-fb-domainAudit.2", + "@labkey/components": "6.41.0-fb-domainAudit.3", "@labkey/themes": "1.4.2" }, "devDependencies": { diff --git a/experiment/package-lock.json b/experiment/package-lock.json index 22c8d4df323..448d49fcf9b 100644 --- a/experiment/package-lock.json +++ b/experiment/package-lock.json @@ -8,7 +8,7 @@ "name": "experiment", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.41.0-fb-domainAudit.2" + "@labkey/components": "6.41.0-fb-domainAudit.3" }, "devDependencies": { "@labkey/build": "8.5.0", @@ -2842,9 +2842,9 @@ } }, "node_modules/@labkey/components": { - "version": "6.41.0-fb-domainAudit.2", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.41.0-fb-domainAudit.2.tgz", - "integrity": "sha512-HIR2JlYkjfDyHy+2+X3iTerdB86ZIEpX9fSMKfnwXghd82Yffz0RXga3qDa2NyrLq/lPpXK26E+krMaUDMxzIg==", + "version": "6.41.0-fb-domainAudit.3", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.41.0-fb-domainAudit.3.tgz", + "integrity": "sha512-vEWzebLZhHUJlvp70vDVW894w8YLc+rA7CqmqVWtXV+gQLnh+bYsDFmwHJF6nrugMUFQs/Clk4sVIQ5YpOrLng==", "dependencies": { "@hello-pangea/dnd": "18.0.1", "@labkey/api": "1.40.1-fb-domainAudit.2", diff --git a/experiment/package.json b/experiment/package.json index 3b38905770c..cb48d3ef243 100644 --- a/experiment/package.json +++ b/experiment/package.json @@ -13,7 +13,7 @@ "test-integration": "cross-env NODE_ENV=test jest --ci --runInBand -c test/js/jest.config.integration.js" }, "dependencies": { - "@labkey/components": "6.41.0-fb-domainAudit.2" + "@labkey/components": "6.41.0-fb-domainAudit.3" }, "devDependencies": { "@labkey/build": "8.5.0", diff --git a/experiment/src/org/labkey/experiment/ExpDataFileListener.java b/experiment/src/org/labkey/experiment/ExpDataFileListener.java index 5853c078bb7..76e3ceab767 100644 --- a/experiment/src/org/labkey/experiment/ExpDataFileListener.java +++ b/experiment/src/org/labkey/experiment/ExpDataFileListener.java @@ -76,7 +76,7 @@ public int fileMoved(@NotNull Path src, @NotNull Path dest, @Nullable User user, // if the data object moved containers, set that as well if (targetContainer != null && !targetContainer.equals(sourceContainer)) data.setContainer(targetContainer); - data.save(user); + data.save(user); // bad!! extra = 1; } diff --git a/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java b/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java index c37ee3f5ca2..b42d9fe92f0 100644 --- a/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java @@ -511,6 +511,7 @@ public void ensureMinGenId(long newSeqValue, Container c) throws ExperimentExcep } } + @Override public String getImportAliasJson() { return _object.getDataParentImportAliasMap(); diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index c53ebf950c0..fd1c053d9c8 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -7973,6 +7973,7 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u String auditUserComment) { ValidationException errors; + StringBuilder changeDetails = new StringBuilder(); // if options doesn't have a rowId value, then it is just coming from the property-editDomain action only only updating domain fields DataClassDomainKindProperties options = properties != null && properties.getRowId() == dataClass.getRowId() ? properties : null; @@ -7988,10 +7989,15 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u validateDataClassName(c, u, newName, oldDataClassName.equalsIgnoreCase(newName)); hasNameChange = true; dataClass.setName(newName); + changeDetails.append("The name of the data class '" + oldDataClassName + "' was changed to '" + newName + "'."); } + changeDetails.append(DomainUtil.getStringPropChangeMsg("Description", dataClass.getDescription(), options.getDescription())); dataClass.setDescription(options.getDescription()); + changeDetails.append(DomainUtil.getStringPropChangeMsg("NameExpression", dataClass.getNameExpression(), options.getNameExpression())); dataClass.setNameExpression(options.getNameExpression()); + changeDetails.append(DomainUtil.getPropChangeMsg("SampleType", dataClass.getSampleType() == null ? null : dataClass.getSampleType().getRowId(), options.getSampleType() == null ? null : options.getSampleType())); dataClass.setSampleType(options.getSampleType()); + changeDetails.append(DomainUtil.getStringPropChangeMsg("Category", dataClass.getCategory(), options.getCategory())); dataClass.setCategory(options.getCategory()); Map> newAliases = options.getImportAliases(); if (newAliases != null && !newAliases.isEmpty()) @@ -8008,7 +8014,18 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u throw new RuntimeException(e); } } + String oldImportAliasJson = null; + try + { + oldImportAliasJson = ExperimentJSONConverter.getImportAliasStringVal(dataClass.getImportAliasMap()); + } + catch (IOException e) + { + throw new RuntimeException(e); + } dataClass.setImportAliasMap(newAliases); + String newImportAliasJson = ExperimentJSONConverter.getImportAliasStringVal(newAliases); + changeDetails.append(DomainUtil.getStringPropChangeMsg("ImportAlias", oldImportAliasJson, newImportAliasJson)); if (!NameExpressionOptionService.get().allowUserSpecifiedNames(c) && options.getNameExpression() == null) throw new ApiUsageException(c.hasProductFolders() ? NAME_EXPRESSION_REQUIRED_MSG_WITH_SUBFOLDERS : NAME_EXPRESSION_REQUIRED_MSG); @@ -8019,15 +8036,11 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u LOG.debug("Saving data class " + dataClass.getName()); dataClass.save(u); - String auditComment = null; SchemaKey schemaKey = SchemaKey.fromParts(ExpSchema.SCHEMA_NAME, DataClassUserSchema.NAME); if (hasNameChange) - { QueryChangeListener.QueryPropertyChange.handleQueryNameChange(oldDataClassName, newName, schemaKey, u, c); - auditComment = "The name of the data class '" + oldDataClassName + "' was changed to '" + newName + "'."; - } - errors = DomainUtil.updateDomainDescriptor(original, update, c, u, hasNameChange, auditComment, auditUserComment);// + errors = DomainUtil.updateDomainDescriptor(original, update, c, u, hasNameChange, changeDetails.toString(), auditUserComment); QueryService.get().saveCalculatedFieldsMetadata(schemaKey.toString(), update.getQueryName(), hasNameChange ? newName : null, update.getCalculatedFields(), !original.getCalculatedFields().isEmpty(), u, c); diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index 3dc4563a88d..0cc08f35da0 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -1005,6 +1005,8 @@ public ValidationException updateSampleType(GWTDomain(impl.getName(), sortOrder)); // Issue 17020: Save any fields whose name changed with a temp, guaranteed unique name. @@ -744,9 +746,9 @@ else if (impl.isNew()) if (isImplNew) propertyAuditInfo.add(new PropertyChangeAuditInfo(impl, true)); - else if (null != pdOld) + else if (pdOld != null) { - PropertyChangeAuditInfo auditInfo = new PropertyChangeAuditInfo(impl, pdOld, oldValidators, oldFormats); + PropertyChangeAuditInfo auditInfo = new PropertyChangeAuditInfo(impl, newPropName, pdOld, oldValidators, oldFormats); if (auditInfo.isChanged()) propertyAuditInfo.add(auditInfo); } @@ -965,6 +967,15 @@ private void addPropertyAuditEvent(@Nullable User user, DomainProperty prop, Str AuditLogService.get().addEvent(user, event); } + public static String getPropertyValidatorStringVal(Collection validators) + { + if (validators == null || validators.isEmpty()) + return ""; + List strings = new ArrayList<>(); + validators.forEach(validator -> strings.add(validator.getStringVal())); + return StringUtils.join(strings, ", "); + } + private static class PropertyChangeAuditInfo { private final DomainProperty _prop; @@ -978,12 +989,12 @@ public PropertyChangeAuditInfo(DomainPropertyImpl prop, boolean isCreated) _details = isCreated ? makeNewPropAuditComment(prop) : ""; } - public PropertyChangeAuditInfo(DomainPropertyImpl prop, PropertyDescriptor pdOld, + public PropertyChangeAuditInfo(DomainPropertyImpl prop, String newPropName, PropertyDescriptor pdOld, String oldValidators, String oldFormats) { _prop = prop; _action = "Modified"; - _details = makeModifiedPropAuditComment(prop, pdOld, oldValidators, oldFormats); + _details = makeModifiedPropAuditComment(prop, newPropName, pdOld, oldValidators, oldFormats); } public DomainProperty getProp() @@ -1045,11 +1056,11 @@ private String makeNewPropAuditComment(DomainProperty prop) return str.toString(); } - private String makeModifiedPropAuditComment(DomainPropertyImpl prop, PropertyDescriptor pdOld, String oldValidators, String oldFormats) + private String makeModifiedPropAuditComment(DomainPropertyImpl prop, String newPropName, PropertyDescriptor pdOld, String oldValidators, String oldFormats) { StringBuilder str = new StringBuilder(); - if (!pdOld.getName().equals(prop.getName())) - str.append("Name: ").append(renderOldVsNew(pdOld.getName(), prop.getName())).append("; "); + if (!pdOld.getName().equals(newPropName)) + str.append("Name: ").append(renderOldVsNew(pdOld.getName(), newPropName)).append("; "); if (!StringUtils.equals(pdOld.getLabel(), prop.getLabel())) str.append("Label: ").append(renderOldVsNew(renderCheckingBlank(pdOld.getLabel()), renderCheckingBlank(prop.getLabel()))).append("; "); if (null != pdOld.getPropertyType() && !pdOld.getPropertyType().equals(prop.getPropertyType())) @@ -1118,21 +1129,13 @@ private String renderCheckingBlank(String value) private static String renderValidators(PropertyDescriptor prop) { Collection validators = DomainPropertyManager.get().getValidators(prop); - if (validators.isEmpty()) - return ""; - List strings = new ArrayList<>(); - validators.forEach(validator -> strings.add(validator.getName() + " [" + - StringUtils.replace(PropertyService.get().getValidatorKind(validator.getTypeURI()).getName(), " Property Validator", "") + "]") - ); - return StringUtils.join(strings, ", "); + return getPropertyValidatorStringVal(validators); } private static String renderConditionalFormats(PropertyDescriptor prop) { List formats = DomainPropertyManager.get().getConditionalFormats(prop); - if (formats.isEmpty()) - return ""; - return Integer.toString(formats.size()); + return ConditionalFormat.toStringVal(formats); } private void renderValidatorsDiff(DomainProperty prop, String oldValidators, StringBuilder str) diff --git a/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java index 1911be81c43..834aea8851e 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java @@ -55,6 +55,7 @@ import org.labkey.api.util.TestContext; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -576,6 +577,9 @@ public void setDefaultValueType(String defaultValueTypeName) if (getDefaultValueType() != null && getDefaultValueType().equals(defaultValueTypeName)) return; + if (getDefaultValueType() == null && defaultValueTypeName == null) + return; // if both are null, don't call edit(), with marks property as dirty + edit().setDefaultValueType(defaultValueTypeName); } @@ -639,6 +643,23 @@ public void setScannable(boolean scannable) edit().setScannable(scannable); } + @Override + public String getPropertyValidatorStringVal() + { + return DomainImpl.getPropertyValidatorStringVal(DomainPropertyManager.get().getValidators(this)); + } + + @Override + public void checkValidatorEdit(String oldValidatorStr, PropertyDescriptor oldPropertyDescriptor) + { + if (isEdited()) + return; + + String newValidatorStr = DomainImpl.getPropertyValidatorStringVal(DomainPropertyManager.get().getValidators(this)); + if (!oldValidatorStr.equals(newValidatorStr)) + _pdOld = oldPropertyDescriptor.clone(); + } + @Override public boolean isScannable() { @@ -1056,6 +1077,12 @@ else if (lookup.getContainer().equals(targetContainer)) @Override public void setConditionalFormats(List formats) { + String newVal = ConditionalFormat.toStringVal(formats); + String oldVal = ConditionalFormat.toStringVal(getConditionalFormats()); + + if (!newVal.equals(oldVal)) + edit(); + _formats = formats; } diff --git a/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java b/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java index 28e31a644b8..30d38ad3fa7 100644 --- a/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java +++ b/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java @@ -15,7 +15,9 @@ */ package org.labkey.experiment.api.property; +import org.apache.commons.lang3.StringUtils; import org.labkey.api.exp.property.IPropertyValidator; +import org.labkey.api.exp.property.PropertyService; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.UnexpectedException; @@ -149,4 +151,10 @@ public final PropertyValidator clone() throw UnexpectedException.wrap(cnse); } } + + public String getStringVal() + { + return getName() + "; " + getExpression() + "; " + getProperties() + "; " + getErrorMessage() + "; " + getDescription() + "; " + + StringUtils.replace(PropertyService.get().getValidatorKind(getTypeURI()).getName(), " Property Validator", ""); + } } \ No newline at end of file diff --git a/list/src/org/labkey/list/model/ListDomainKind.java b/list/src/org/labkey/list/model/ListDomainKind.java index 8e8f2ae5bc4..89678239c23 100644 --- a/list/src/org/labkey/list/model/ListDomainKind.java +++ b/list/src/org/labkey/list/model/ListDomainKind.java @@ -462,6 +462,7 @@ public Domain createDomain(GWTDomain domain, ListDomainKindProperties listProper try (DbScope.Transaction transaction = ListManager.get().getListMetadataSchema().getScope().ensureTransaction()) { exception = new ValidationException(); + StringBuilder changeDetails = new StringBuilder(); Domain domain = PropertyService.get().getDomain(container, original.getDomainURI()); if (null == domain) @@ -496,7 +497,6 @@ else if (!key.getName().equalsIgnoreCase(newKey.getName())) //handle name change String updatedName = StringUtils.trim(update.getName()); boolean hasNameChange = !original.getName().equals(updatedName); - String auditComment = null; if (hasNameChange) { if (updatedName.length() > MAX_NAME_LENGTH) @@ -507,7 +507,7 @@ else if (ListService.get().getList(container, updatedName, false) != null) { return exception.addGlobalError("The name '" + updatedName + "' is already in use."); } - auditComment = "The name of the list domain '" + original.getName() + "' was changed to '" + updatedName + "'."; + changeDetails.append("The name of the list domain '" + original.getName() + "' was changed to '" + updatedName + "'."); } //return if there are errors before moving forward with the save @@ -524,14 +524,14 @@ else if (ListService.get().getList(container, updatedName, false) != null) if (!original.getDomainURI().equals(update.getDomainURI())) return exception.addGlobalError("domainURI mismatch between old and new domain"); - updateListProperties(container, user, listDefinition.getListId(), listProperties); + changeDetails.append(updateListProperties(container, user, listDefinition.getListId(), listProperties)); } // Issue 45042: Allow for the list description to be set via the save domain API calls else if (update.getDescription() != null) { listProperties = getListProperties(container, user, listDefinition.getListId()); listProperties.setDescription(update.getDescription()); - updateListProperties(container, user, listDefinition.getListId(), listProperties); + changeDetails.append(updateListProperties(container, user, listDefinition.getListId(), listProperties)); } //update domain design properties @@ -576,7 +576,7 @@ else if (update.getDescription() != null) } //update domain properties - exception.addErrors(DomainUtil.updateDomainDescriptor(original, update, container, user, hasNameChange, auditComment, auditUserComment)); + exception.addErrors(DomainUtil.updateDomainDescriptor(original, update, container, user, hasNameChange, changeDetails.toString(), auditUserComment)); QueryService.get().saveCalculatedFieldsMetadata(ListQuerySchema.NAME, update.getQueryName(), hasNameChange ? update.getName() : null, update.getCalculatedFields(), !original.getCalculatedFields().isEmpty(), user, container); } @@ -611,43 +611,66 @@ private ListDomainKindProperties getListProperties(Container container, User use return new TableSelector(ListManager.get().getListMetadataTable(), filter, null).getObject(ListDomainKindProperties.class); } - private void updateListProperties(Container container, User user, int listId, ListDomainKindProperties listProperties) + private String updateListProperties(Container container, User user, int listId, ListDomainKindProperties listProperties) { ListDomainKindProperties existingListProps = getListProperties(container, user, listId); //merge existing and new properties - ListDomainKindProperties updatedListProps = updateListProperties(existingListProps, listProperties); + Pair updatedListProps = updateListProperties(existingListProps, listProperties); - ListManager.get().update(user, container, updatedListProps); + ListManager.get().update(user, container, updatedListProps.first); + + return updatedListProps.second; } //updates list properties except listId, domainId, keyName, keyType, and lastIndexed - private ListDomainKindProperties updateListProperties(ListDomainKindProperties existingListProps, ListDomainKindProperties newListProps) + private Pair updateListProperties(ListDomainKindProperties existingListProps, ListDomainKindProperties newListProps) { - ListDomainKindProperties updatedListProps = new ListDomainKindProperties(existingListProps); + StringBuilder changeDetails = new StringBuilder(); + ListDomainKindProperties updatedListProps = new ListDomainKindProperties(existingListProps); if (null != newListProps.getName()) + { updatedListProps.setName(newListProps.getName().trim()); + changeDetails.append(DomainUtil.getPropChangeMsg("Name", existingListProps.getName(), newListProps.getName())); + } + changeDetails.append(DomainUtil.getPropChangeMsg("TitleColumn", existingListProps.getTitleColumn(), newListProps.getTitleColumn())); updatedListProps.setTitleColumn(newListProps.getTitleColumn()); + changeDetails.append(DomainUtil.getPropChangeMsg("Description", existingListProps.getDescription(), newListProps.getDescription())); updatedListProps.setDescription(newListProps.getDescription()); + changeDetails.append(DomainUtil.getPropChangeMsg("AllowDelete", existingListProps.isAllowDelete(), newListProps.isAllowDelete())); updatedListProps.setAllowDelete(newListProps.isAllowDelete()); + changeDetails.append(DomainUtil.getPropChangeMsg("AllowUpload", existingListProps.isAllowUpload(), newListProps.isAllowUpload())); updatedListProps.setAllowUpload(newListProps.isAllowUpload()); + changeDetails.append(DomainUtil.getPropChangeMsg("AllowExport", existingListProps.isAllowExport(), newListProps.isAllowExport())); updatedListProps.setAllowExport(newListProps.isAllowExport()); + changeDetails.append(DomainUtil.getPropChangeMsg("DiscussionSetting", existingListProps.getDiscussionSetting(), newListProps.getDiscussionSetting())); updatedListProps.setDiscussionSetting(newListProps.getDiscussionSetting()); + changeDetails.append(DomainUtil.getPropChangeMsg("Category", existingListProps.getCategory(), newListProps.getCategory())); updatedListProps.setCategory(newListProps.getCategory()); + changeDetails.append(DomainUtil.getPropChangeMsg("EntireListTitleTemplate", existingListProps.getEntireListTitleTemplate(), newListProps.getEntireListTitleTemplate())); updatedListProps.setEntireListTitleTemplate(newListProps.getEntireListTitleTemplate()); + changeDetails.append(DomainUtil.getPropChangeMsg("EntireListIndexSetting", existingListProps.getEntireListIndexSetting(), newListProps.getEntireListIndexSetting())); updatedListProps.setEntireListIndexSetting(newListProps.getEntireListIndexSetting()); + changeDetails.append(DomainUtil.getPropChangeMsg("EntireListBodySetting", existingListProps.getEntireListBodySetting(), newListProps.getEntireListBodySetting())); updatedListProps.setEntireListBodySetting(newListProps.getEntireListBodySetting()); + changeDetails.append(DomainUtil.getPropChangeMsg("EachItemTitleTemplate", existingListProps.getEachItemTitleTemplate(), newListProps.getEachItemTitleTemplate())); updatedListProps.setEachItemTitleTemplate(newListProps.getEachItemTitleTemplate()); + changeDetails.append(DomainUtil.getPropChangeMsg("EachItemBodySetting", existingListProps.getEachItemBodySetting(), newListProps.getEachItemBodySetting())); updatedListProps.setEachItemBodySetting(newListProps.getEachItemBodySetting()); + changeDetails.append(DomainUtil.getPropChangeMsg("EntireListIndex", existingListProps.isEntireListIndex(), newListProps.isEntireListIndex())); updatedListProps.setEntireListIndex(newListProps.isEntireListIndex()); + changeDetails.append(DomainUtil.getPropChangeMsg("EntireListBodyTemplate", existingListProps.getEntireListBodyTemplate(), newListProps.getEntireListBodyTemplate())); updatedListProps.setEntireListBodyTemplate(newListProps.getEntireListBodyTemplate()); + changeDetails.append(DomainUtil.getPropChangeMsg("EachItemIndex", existingListProps.isEachItemIndex(), newListProps.isEachItemIndex())); updatedListProps.setEachItemIndex(newListProps.isEachItemIndex()); + changeDetails.append(DomainUtil.getPropChangeMsg("EachItemBodyTemplate", existingListProps.getEachItemBodyTemplate(), newListProps.getEachItemBodyTemplate())); updatedListProps.setEachItemBodyTemplate(newListProps.getEachItemBodyTemplate()); + changeDetails.append(DomainUtil.getPropChangeMsg("FileAttachmentIndex", existingListProps.isFileAttachmentIndex(), newListProps.isFileAttachmentIndex())); updatedListProps.setFileAttachmentIndex(newListProps.isFileAttachmentIndex()); - return updatedListProps; + return new Pair<>(updatedListProps, changeDetails.toString()); } private GWTPropertyDescriptor findField(String name, List fields) diff --git a/query/src/org/labkey/query/controllers/QueryController.java b/query/src/org/labkey/query/controllers/QueryController.java index 2f57629f5af..2864b3062f0 100644 --- a/query/src/org/labkey/query/controllers/QueryController.java +++ b/query/src/org/labkey/query/controllers/QueryController.java @@ -8228,6 +8228,7 @@ public static class QueryImportTemplateForm { private String schemaName; private String queryName; + private String auditUserComment; private List templateLabels; private List templateUrls; private Long _lastKnownModified; @@ -8283,6 +8284,16 @@ public void setLastKnownModified(Long lastKnownModified) _lastKnownModified = lastKnownModified; } + public String getAuditUserComment() + { + return auditUserComment; + } + + public void setAuditUserComment(String auditUserComment) + { + this.auditUserComment = auditUserComment; + } + } @Marshal(Marshaller.Jackson) @@ -8517,6 +8528,7 @@ public Object execute(QueryImportTemplateForm form, BindException errors) throws } DomainAuditProvider.DomainAuditEvent event = new DomainAuditProvider.DomainAuditEvent(getContainer(), "Import templates updated."); + event.setUserComment(form.getAuditUserComment()); event.setDomainUri(_domain.getTypeURI()); event.setDomainName(_domain.getName()); AuditLogService.get().addEvent(user, event); diff --git a/study/src/org/labkey/study/model/DatasetDomainKind.java b/study/src/org/labkey/study/model/DatasetDomainKind.java index acd5c34ebb0..500deaaf37b 100644 --- a/study/src/org/labkey/study/model/DatasetDomainKind.java +++ b/study/src/org/labkey/study/model/DatasetDomainKind.java @@ -646,11 +646,30 @@ private void checkCanUpdate(DatasetDefinition def, Container container, User use } } - private ValidationException updateDomainDescriptor(GWTDomain original, GWTDomain update, - Container container, User user) + private @NotNull ValidationException updateDomainDescriptor(GWTDomain original, GWTDomain update, + @Nullable DatasetDefinition oldDef, DatasetDomainKindProperties datasetPropertiesUpdate, Container container, User user, String userComment) { + StringBuilder changeDetails = new StringBuilder(); + boolean hasNameChange = false; + if (oldDef != null) + { + hasNameChange = !datasetPropertiesUpdate.getName().equals(oldDef.getName()); + if (hasNameChange) + changeDetails.append("The name of the dataset '" + oldDef.getName() + "' was changed to '" + datasetPropertiesUpdate.getName() + "'."); + changeDetails.append(DomainUtil.getStringPropChangeMsg("Description", oldDef.getDescription(), datasetPropertiesUpdate.getDescription())); + changeDetails.append(DomainUtil.getStringPropChangeMsg("Category", oldDef.getCategory(), datasetPropertiesUpdate.getCategory())); + changeDetails.append(DomainUtil.getStringPropChangeMsg("Label", oldDef.getLabel(), datasetPropertiesUpdate.getLabel())); + changeDetails.append(DomainUtil.getStringPropChangeMsg("KeyPropertyName", oldDef.getKeyPropertyName(), datasetPropertiesUpdate.getKeyPropertyName())); + changeDetails.append(DomainUtil.getStringPropChangeMsg("VisitDateColumnName", oldDef.getVisitDateColumnName(), datasetPropertiesUpdate.getVisitDatePropertyName())); + changeDetails.append(DomainUtil.getStringPropChangeMsg("Tag", oldDef.getTag(), datasetPropertiesUpdate.getTag())); + changeDetails.append(DomainUtil.getPropChangeMsg("KeyPropertyManaged", oldDef.getKeyManagementType() != Dataset.KeyManagementType.None, datasetPropertiesUpdate.isKeyPropertyManaged())); + changeDetails.append(DomainUtil.getPropChangeMsg("IsDemographicData", oldDef.isDemographicData(), datasetPropertiesUpdate.isDemographicData())); + changeDetails.append(DomainUtil.getPropChangeMsg("IsUseTimeKeyField", oldDef.getUseTimeKeyField(), datasetPropertiesUpdate.isUseTimeKeyField())); + changeDetails.append(DomainUtil.getPropChangeMsg("CohortId", oldDef.getCohortId(), datasetPropertiesUpdate.getCohortId())); + } + ValidationException exception = new ValidationException(); - exception.addErrors(DomainUtil.updateDomainDescriptor(original, update, container, user)); + exception.addErrors(DomainUtil.updateDomainDescriptor(original, update, container, user, hasNameChange, changeDetails.toString(), userComment)); return exception; } @@ -736,7 +755,7 @@ private ValidationException updateDataset(DatasetDomainKindProperties datasetPro Lock[] locks = def == null ? new Lock[0] : new Lock[] { def.getDomainLoadingLock() }; try (DbScope.Transaction transaction = StudySchema.getInstance().getScope().ensureTransaction(locks)) { - ValidationException exception = updateDomainDescriptor(original, update, container, user); + ValidationException exception = updateDomainDescriptor(original, update, def, datasetProperties, container, user, userComment); QueryService.get().saveCalculatedFieldsMetadata("study", update.getQueryName(), hasNameChange ? datasetProperties.getName() : null, update.getCalculatedFields(), !original.getCalculatedFields().isEmpty(), user, container); From 6257385059b910b984ef8f480dd2ae83e1d2a1fd Mon Sep 17 00:00:00 2001 From: XingY Date: Sun, 11 May 2025 22:32:42 -0700 Subject: [PATCH 03/30] clean --- api/src/org/labkey/api/exp/property/DomainUtil.java | 2 +- .../labkey/api/issues/AbstractIssuesListDefDomainKind.java | 2 +- api/src/org/labkey/api/query/ExtendedTableDomainKind.java | 2 +- .../org/labkey/api/reports/model/ReportPropsManager.java | 2 +- .../labkey/assay/plate/AssayPlateMetadataServiceImpl.java | 2 +- assay/src/org/labkey/assay/plate/PlateManager.java | 4 ++-- .../src/org/labkey/assay/plate/PlateMetadataDomainKind.java | 2 +- core/src/org/labkey/core/query/UsersDomainKind.java | 4 ++-- .../src/org/labkey/experiment/api/ExpDataClassImpl.java | 2 +- .../src/org/labkey/experiment/api/ExpSampleTypeImpl.java | 4 ++-- .../org/labkey/experiment/api/ExperimentServiceImpl.java | 2 +- .../org/labkey/experiment/api/SampleTypeServiceImpl.java | 2 +- .../experiment/controllers/property/PropertyController.java | 2 +- list/src/org/labkey/list/model/ListDefinitionImpl.java | 2 +- list/src/org/labkey/list/model/ListImporter.java | 4 ++-- .../labkey/api/specimen/model/SpecimenTablesProvider.java | 2 +- study/src/org/labkey/study/assay/StudyPublishManager.java | 6 +++--- study/src/org/labkey/study/controllers/StudyController.java | 2 +- .../labkey/study/controllers/StudyDefinitionController.java | 2 +- .../org/labkey/study/dataset/DatasetSnapshotProvider.java | 2 +- 20 files changed, 26 insertions(+), 26 deletions(-) diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index 7cec362abe3..a984c8d04e6 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -924,7 +924,7 @@ public static ValidationException updateDomainDescriptor(GWTDomain> propTextChoiceValueUpdates = updatePropertyValidators(p, old, pd); // + List> propTextChoiceValueUpdates = updatePropertyValidators(p, old, pd); if (propTextChoiceValueUpdates != null) textChoiceValueUpdates.put(p, propTextChoiceValueUpdates); if (old.equals(pd)) diff --git a/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java b/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java index 5b3bea29cbb..b299c893f7f 100644 --- a/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java +++ b/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java @@ -293,7 +293,7 @@ public Class getTypeClass() if (options != null && StringUtils.isBlank(options.getIssueDefName())) return new ValidationException("Issue name must not be null."); - return IssuesListDefService.get().updateIssueDefinition(container, user, original, update, options);// + return IssuesListDefService.get().updateIssueDefinition(container, user, original, update, options); } @Override diff --git a/api/src/org/labkey/api/query/ExtendedTableDomainKind.java b/api/src/org/labkey/api/query/ExtendedTableDomainKind.java index 5e543c1514a..5b91f78925c 100644 --- a/api/src/org/labkey/api/query/ExtendedTableDomainKind.java +++ b/api/src/org/labkey/api/query/ExtendedTableDomainKind.java @@ -106,7 +106,7 @@ public Domain createDomain(GWTDomain gwtDomain, JSONObjec { DomainUtil.addProperty(newDomain, pd, defaultValues, propertyUris, null); } - newDomain.save(user);// + newDomain.save(user); } catch (ChangePropertyDescriptorException e) { diff --git a/api/src/org/labkey/api/reports/model/ReportPropsManager.java b/api/src/org/labkey/api/reports/model/ReportPropsManager.java index 00eb6b6e17f..cd535d89f19 100644 --- a/api/src/org/labkey/api/reports/model/ReportPropsManager.java +++ b/api/src/org/labkey/api/reports/model/ReportPropsManager.java @@ -125,7 +125,7 @@ public synchronized DomainProperty ensureProperty(Container container, User user } if (dirty) - domain.save(user);// + domain.save(user); return dp; } diff --git a/assay/src/org/labkey/assay/plate/AssayPlateMetadataServiceImpl.java b/assay/src/org/labkey/assay/plate/AssayPlateMetadataServiceImpl.java index b4e7e9d619b..a03bbd57d67 100644 --- a/assay/src/org/labkey/assay/plate/AssayPlateMetadataServiceImpl.java +++ b/assay/src/org/labkey/assay/plate/AssayPlateMetadataServiceImpl.java @@ -1073,7 +1073,7 @@ else if (wasValidType) { try { - replicateDomain.save(user);// + replicateDomain.save(user); } catch (ExperimentException e) { diff --git a/assay/src/org/labkey/assay/plate/PlateManager.java b/assay/src/org/labkey/assay/plate/PlateManager.java index 07660210f1b..32a31e8d019 100644 --- a/assay/src/org/labkey/assay/plate/PlateManager.java +++ b/assay/src/org/labkey/assay/plate/PlateManager.java @@ -2329,7 +2329,7 @@ public Container getPlateMetadataDomainContainer(Container container) DomainUtil.addProperty(metadataDomain, pd, new HashMap<>(), new HashSet<>(), null); } - metadataDomain.save(user);// + metadataDomain.save(user); tx.commit(); } } @@ -2376,7 +2376,7 @@ public Container getPlateMetadataDomainContainer(Container container) if (dp != null) dp.delete(); } - metadataDomain.save(user);// + metadataDomain.save(user); tx.commit(); } } diff --git a/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java b/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java index d8e47217146..a3e461423a2 100644 --- a/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java +++ b/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java @@ -129,7 +129,7 @@ public Domain createDomain(GWTDomain domain, JSONObject a String domainURI = generateDomainURI(container); Domain metadataDomain = PropertyService.get().createDomain(container, domainURI, domain.getName(), templateInfo); ensureDomainProperties(metadataDomain, container); - metadataDomain.save(user);// + metadataDomain.save(user); return PropertyService.get().getDomain(container, domainURI); } diff --git a/core/src/org/labkey/core/query/UsersDomainKind.java b/core/src/org/labkey/core/query/UsersDomainKind.java index 7f2c1c7dd90..4f6664a46f2 100644 --- a/core/src/org/labkey/core/query/UsersDomainKind.java +++ b/core/src/org/labkey/core/query/UsersDomainKind.java @@ -211,7 +211,7 @@ public static void ensureDomain(ModuleContext context) if (domain == null) { domain = PropertyService.get().createDomain(UsersDomainKind.getDomainContainer(), domainURI, CoreQuerySchema.USERS_TABLE_NAME); - domain.save(user);// + domain.save(user); } // ensure required fields @@ -240,7 +240,7 @@ else if (!existingProp.getName().equals(pd.getName())) } if (dirty) - domain.save(user);// + domain.save(user); transaction.commit(); } catch (Exception e) diff --git a/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java b/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java index b42d9fe92f0..d9006a21c23 100644 --- a/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java @@ -269,7 +269,7 @@ public void save(User user) domain = PropertyService.get().createDomain(getContainer(), getLSID(), getName()); try { - domain.save(user);// + domain.save(user); } catch (ChangePropertyDescriptorException e) { diff --git a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java index 35e35d37518..13dedf46472 100644 --- a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java @@ -813,7 +813,7 @@ public Domain getDomain() _domain = PropertyService.get().createDomain(getContainer(), getLSID(), getName()); try { - _domain.save(null);// + _domain.save(null); } catch (ChangePropertyDescriptorException e) { @@ -899,7 +899,7 @@ public void save(User user) domain = PropertyService.get().createDomain(getContainer(), getLSID(), getName()); try { - domain.save(user);// + domain.save(user); } catch (ChangePropertyDescriptorException e) { diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index fd1c053d9c8..72787d1b254 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -7939,7 +7939,7 @@ else if (domain.getPropertyByName(propertyName) != null) // issue 25275 if (kind != null) domain.setPropertyForeignKeys(kind.getPropertyForeignKeys(c)); - domain.save(u);// + domain.save(u); impl.save(u); SchemaKey schemaKey = SchemaKey.fromParts(ExpSchema.SCHEMA_NAME, DataClassUserSchema.NAME); diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index 0cc08f35da0..0630bd40eda 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -880,7 +880,7 @@ public ExpSampleTypeImpl createSampleType(Container c, User u, String name, Stri { try { - domain.save(u);// + domain.save(u); st.save(u); QueryService.get().saveCalculatedFieldsMetadata(SamplesSchema.SCHEMA_NAME, name, null, calculatedFields, false, u, c); DefaultValueService.get().setDefaultValues(domain.getContainer(), defaultValues); diff --git a/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java b/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java index 0daddef5677..dbf10d72403 100644 --- a/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java +++ b/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java @@ -201,7 +201,7 @@ public ModelAndView getView(DomainForm form, BindException errors) // save the domain so that we ensure it exists before we try to edit it try (var ignored = SpringActionController.ignoreSqlUpdates()) { - _domain.save(getUser());// + _domain.save(getUser()); } catch (ChangePropertyDescriptorException e) { diff --git a/list/src/org/labkey/list/model/ListDefinitionImpl.java b/list/src/org/labkey/list/model/ListDefinitionImpl.java index cf3e145bc5f..29398ba717a 100644 --- a/list/src/org/labkey/list/model/ListDefinitionImpl.java +++ b/list/src/org/labkey/list/model/ListDefinitionImpl.java @@ -396,7 +396,7 @@ public void save(User user, boolean ensureKey) throws Exception // The domain kind cannot lookup the list definition if the domain has not been saved ((ListDomainKind) domain.getDomainKind()).setListDefinition(this); - domain.save(user);// + domain.save(user); _def.setDomainId(domain.getTypeId()); ListDef inserted = ListManager.get().insert(user, _def, _preferredListIds); diff --git a/list/src/org/labkey/list/model/ListImporter.java b/list/src/org/labkey/list/model/ListImporter.java index 0ef586c772b..e68bd4d2d67 100644 --- a/list/src/org/labkey/list/model/ListImporter.java +++ b/list/src/org/labkey/list/model/ListImporter.java @@ -657,7 +657,7 @@ public void process() throws Exception } } if (hasValidator) - domain.save(_user);// + domain.save(_user); } } } @@ -714,7 +714,7 @@ else if (!listDef.getKeyName().equals(columnName) && !currentColumns.get(columnN try { if (isDirty) - domain.save(user);// + domain.save(user); } catch (Exception e) { diff --git a/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java b/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java index fafbce9bd0a..868dad573d8 100644 --- a/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java +++ b/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java @@ -111,7 +111,7 @@ public final Domain getDomain(String tableName, boolean create) } domain.setPropertyForeignKeys(domainKind.getPropertyForeignKeys(_container, SpecimenTablesProvider.this)); - domain.save(_user);// + domain.save(_user); // Refresh the domain. save() doesn't populate provisioned schema and table names, e.g. return PropertyService.get().getDomain(_container, domainURI); diff --git a/study/src/org/labkey/study/assay/StudyPublishManager.java b/study/src/org/labkey/study/assay/StudyPublishManager.java index f34bba47fd1..41818beb073 100644 --- a/study/src/org/labkey/study/assay/StudyPublishManager.java +++ b/study/src/org/labkey/study/assay/StudyPublishManager.java @@ -725,7 +725,7 @@ private Map ensurePropertyDescriptors( if (domain == null) { domain = PropertyService.get().createDomain(dataset.getContainer(), dataset.getTypeURI(), dataset.getName()); - domain.save(user);// + domain.save(user); } // Strip out any spaces from existing PropertyDescriptors in the dataset boolean propertyChanged = false; @@ -740,7 +740,7 @@ private Map ensurePropertyDescriptors( } if (propertyChanged) { - domain.save(user);// + domain.save(user); } // Strip out spaces from any proposed PropertyDescriptor names @@ -862,7 +862,7 @@ private boolean renameRunPropertyToBatch(Domain domain, Map prop property.setPropertyURI(property.getPropertyURI().replace("#Run", "#Batch")); propertyNamesToUris.remove(oldName); propertyNamesToUris.put(newPdName, property.getPropertyURI()); - domain.save(user);// + domain.save(user); return true; } } diff --git a/study/src/org/labkey/study/controllers/StudyController.java b/study/src/org/labkey/study/controllers/StudyController.java index 4a77d5616a3..cae84a22dad 100644 --- a/study/src/org/labkey/study/controllers/StudyController.java +++ b/study/src/org/labkey/study/controllers/StudyController.java @@ -5152,7 +5152,7 @@ private Dataset createDataset(StudySnapshotForm form, BindException errors) thro { DatasetSnapshotProvider.addAsDomainProperty(d, col); } - d.save(getUser());// + d.save(getUser()); return def; } diff --git a/study/src/org/labkey/study/controllers/StudyDefinitionController.java b/study/src/org/labkey/study/controllers/StudyDefinitionController.java index 3cc29d2fc79..27f4b31f5e7 100644 --- a/study/src/org/labkey/study/controllers/StudyDefinitionController.java +++ b/study/src/org/labkey/study/controllers/StudyDefinitionController.java @@ -74,7 +74,7 @@ public boolean handlePost(ReturnUrlForm form, BindException errors) throws Excep if (_domain == null) { _domain = PropertyService.get().createDomain(getContainer(), domainURI, domainInfo.getDomainName()); - _domain.save(getUser());// + _domain.save(getUser()); } return true; diff --git a/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java b/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java index 99ee1178d34..24723507f4a 100644 --- a/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java +++ b/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java @@ -575,7 +575,7 @@ private void updateDomainProperties(User user, QuerySnapshotDefinition snapshotD } } if (dirty) - snapshotDomain.save(user);// + snapshotDomain.save(user); } } } From 48424380a6755cde374f8d218ca174a21d6db82f Mon Sep 17 00:00:00 2001 From: XingY Date: Mon, 12 May 2025 14:39:59 -0700 Subject: [PATCH 04/30] switch to use domain.delete for study dataset delete --- api/src/org/labkey/api/study/Dataset.java | 2 ++ .../experiment/api/ExperimentServiceImpl.java | 2 +- .../experiment/api/SampleTypeServiceImpl.java | 2 +- .../study/controllers/DatasetController.java | 2 +- .../study/controllers/StudyController.java | 4 +-- .../labkey/study/model/DatasetDefinition.java | 9 +++++-- .../labkey/study/model/DatasetDomainKind.java | 4 +-- .../org/labkey/study/model/StudyManager.java | 27 ++++++------------- 8 files changed, 24 insertions(+), 28 deletions(-) diff --git a/api/src/org/labkey/api/study/Dataset.java b/api/src/org/labkey/api/study/Dataset.java index b44108ecaeb..555458ea47d 100644 --- a/api/src/org/labkey/api/study/Dataset.java +++ b/api/src/org/labkey/api/study/Dataset.java @@ -364,6 +364,8 @@ public String getRecallFromStudyAuditMessage(String label, int recordCount) void delete(User user); + void delete(User user, String auditUserComment); + void deleteAllRows(User user); /** diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index 72787d1b254..5da569d9bd2 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -4552,7 +4552,7 @@ public void deleteProtocolByRowIds(Container c, User user, String auditUserComme { for (Dataset dataset : StudyPublishService.get().getDatasetsForPublishSource(protocolToDelete.getRowId(), Dataset.PublishSource.Assay)) { - dataset.delete(user); + dataset.delete(user, auditUserComment); } } else diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index 0630bd40eda..0f6ada63a5b 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -596,7 +596,7 @@ public void deleteSampleType(int rowId, Container c, User user, @Nullable String { for (Dataset dataset : StudyPublishService.get().getDatasetsForPublishSource(rowId, Dataset.PublishSource.SampleType)) { - dataset.delete(user); + dataset.delete(user, auditUserComment); } } else diff --git a/study/src/org/labkey/study/controllers/DatasetController.java b/study/src/org/labkey/study/controllers/DatasetController.java index 2b901dcfd52..394cdf23fc8 100644 --- a/study/src/org/labkey/study/controllers/DatasetController.java +++ b/study/src/org/labkey/study/controllers/DatasetController.java @@ -225,7 +225,7 @@ public boolean handlePost(DatasetDeleteForm form, BindException errors) { if (!def.canDeleteDefinition(getUser())) continue; - StudyManager.getInstance().deleteDataset(study, getUser(), def, false); + StudyManager.getInstance().deleteDataset(study, getUser(), def, false, null); transaction.commit(); countDeleted++; } diff --git a/study/src/org/labkey/study/controllers/StudyController.java b/study/src/org/labkey/study/controllers/StudyController.java index ec5232c0dd7..8ab653e7399 100644 --- a/study/src/org/labkey/study/controllers/StudyController.java +++ b/study/src/org/labkey/study/controllers/StudyController.java @@ -4742,7 +4742,7 @@ public boolean handlePost(IdForm form, BindException errors) throws Exception try (DbScope.Transaction transaction = scope.ensureTransaction()) { // performStudyResync==false so we can do this out of the transaction - StudyManager.getInstance().deleteDataset(getStudyRedirectIfNull(), getUser(), ds, false); + StudyManager.getInstance().deleteDataset(getStudyRedirectIfNull(), getUser(), ds, false, null); transaction.commit(); } @@ -5056,7 +5056,7 @@ private void deletePreviousDatasetDefinition(StudySnapshotForm form) DatasetDefinition dsDef = StudyManager.getInstance().getDatasetDefinition(study, form.getSnapshotDatasetId()); if (dsDef != null) { - StudyManager.getInstance().deleteDataset(study, getUser(), dsDef, true); + StudyManager.getInstance().deleteDataset(study, getUser(), dsDef, true, null); form.setSnapshotDatasetId(-1); } } diff --git a/study/src/org/labkey/study/model/DatasetDefinition.java b/study/src/org/labkey/study/model/DatasetDefinition.java index 6d3a77e0aa2..2532a12f797 100644 --- a/study/src/org/labkey/study/model/DatasetDefinition.java +++ b/study/src/org/labkey/study/model/DatasetDefinition.java @@ -1152,15 +1152,20 @@ public boolean hasMatchingExtraKey(Dataset other) } @Override - public void delete(User user) + public void delete(User user, String auditUserComment) { if (!canDeleteDefinition(user)) { throw new UnauthorizedException("No permission to delete dataset " + getName() + " for study in " + getContainer().getPath()); } - StudyManager.getInstance().deleteDataset(getStudy(), user, this, true); + StudyManager.getInstance().deleteDataset(getStudy(), user, this, true, auditUserComment); } + @Override + public void delete(User user) + { + delete(user, null); + } @Override public void deleteAllRows(User user) diff --git a/study/src/org/labkey/study/model/DatasetDomainKind.java b/study/src/org/labkey/study/model/DatasetDomainKind.java index 500deaaf37b..bc2a6fc1636 100644 --- a/study/src/org/labkey/study/model/DatasetDomainKind.java +++ b/study/src/org/labkey/study/model/DatasetDomainKind.java @@ -779,7 +779,7 @@ private ValidationException updateDataset(DatasetDomainKindProperties datasetPro } @Override - public void deleteDomain(User user, Domain domain, String userComment)// + public void deleteDomain(User user, Domain domain, String auditUserComment) { DatasetDefinition def = StudyManager.getInstance().getDatasetDefinition(domain.getTypeURI()); if (def == null) @@ -791,7 +791,7 @@ public void deleteDomain(User user, Domain domain, String userComment)// try (DbScope.Transaction transaction = StudySchema.getInstance().getSchema().getScope().ensureTransaction()) { - StudyManager.getInstance().deleteDataset(study, user, def, false); + StudyManager.getInstance().deleteDataset(study, user, def, false, auditUserComment); transaction.commit(); } } diff --git a/study/src/org/labkey/study/model/StudyManager.java b/study/src/org/labkey/study/model/StudyManager.java index 6f7e1094c5b..f26b6c70185 100644 --- a/study/src/org/labkey/study/model/StudyManager.java +++ b/study/src/org/labkey/study/model/StudyManager.java @@ -2549,7 +2549,7 @@ public int purgeDataset(DatasetDefinition dataset, @Nullable Date cutoff) * @param performStudyResync whether to kick off our normal bookkeeping. If the whole study is being deleted, * we don't need to bother doing this, for example. */ - public void deleteDataset(StudyImpl study, User user, DatasetDefinition ds, boolean performStudyResync) + public void deleteDataset(StudyImpl study, User user, DatasetDefinition ds, boolean performStudyResync, String auditUserComment) { try (Transaction transaction = StudySchema.getInstance().getScope().ensureTransaction()) { @@ -2580,10 +2580,10 @@ public void deleteDataset(StudyImpl study, User user, DatasetDefinition ds, bool } }); - - deleteDatasetType(study, user, ds); try { + deleteDatasetType(study, user, ds, auditUserComment); + QuerySnapshotDefinition def = QueryService.get().getSnapshotDef(study.getContainer(), StudySchema.getInstance().getSchemaName(), ds.getName()); if (def != null) def.delete(user); @@ -2631,7 +2631,7 @@ public void deleteDataset(StudyImpl study, User user, DatasetDefinition ds, bool /** delete a dataset type and data * does not clear typeURI as we're about to delete the dataset */ - private void deleteDatasetType(Study study, User user, DatasetDefinition ds) + private void deleteDatasetType(Study study, User user, DatasetDefinition ds, String auditUserComment) throws DomainNotFoundException { assert StudySchema.getInstance().getSchema().getScope().isTransactionActive(); @@ -2641,19 +2641,8 @@ private void deleteDatasetType(Study study, User user, DatasetDefinition ds) if (!ds.canDeleteDefinition(user)) throw new IllegalStateException("Can't delete dataset: " + ds.getName()); - StorageProvisioner.get().drop(ds.getDomain()); - - if (ds.getTypeURI() != null) - { - try - { - OntologyManager.deleteType(ds.getTypeURI(), study.getContainer()); - } - catch (DomainNotFoundException x) - { - // continue - } - } + if (ds.getDomain() != null) + ds.getDomain().delete(user, auditUserComment); } // Any container can be passed here (whether it contains a study or not). @@ -2702,7 +2691,7 @@ public void deleteAllStudyData(Container c, User user) for (DatasetDefinition dsd : dsds) { if (dsd.getContainer().equals(dsd.getDefinitionContainer())) - deleteDataset(study, user, dsd, false); + deleteDataset(study, user, dsd, false, null); else dsd.deleteAllRows(user); } @@ -4632,7 +4621,7 @@ public DatasetDefinition linkPlaceHolderDataset(StudyImpl study, User user, Data String label = expectationDataset.getLabel(); // no need to resync the study, as there should be no data in the expectation dataset - deleteDataset(study, user, expectationDataset, false); + deleteDataset(study, user, expectationDataset, false, null); targetDataset = targetDataset.createMutable(); targetDataset.setName(name); From ca526b9f8961babd86055b3eec58fb16006e1797 Mon Sep 17 00:00:00 2001 From: XingY Date: Mon, 12 May 2025 23:38:32 -0700 Subject: [PATCH 05/30] fix regex/validator, add selenium tests --- .../api/exp/property/DomainProperty.java | 2 +- .../labkey/api/exp/property/DomainUtil.java | 35 ++++++++++++++++--- .../experiment/api/property/DomainImpl.java | 2 +- .../api/property/DomainPropertyImpl.java | 6 ++-- .../api/property/PropertyValidator.java | 10 +++++- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/api/src/org/labkey/api/exp/property/DomainProperty.java b/api/src/org/labkey/api/exp/property/DomainProperty.java index 633560b1644..2e0211eb201 100644 --- a/api/src/org/labkey/api/exp/property/DomainProperty.java +++ b/api/src/org/labkey/api/exp/property/DomainProperty.java @@ -158,5 +158,5 @@ default boolean isUniqueIdField() void setScannable(boolean scannable); String getPropertyValidatorStringVal(); - void checkValidatorEdit(String oldValidatorStr, PropertyDescriptor oldPropertyDescriptor); + void setOldPropertyDescriptor(PropertyDescriptor oldPropertyDescriptor); } diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index a984c8d04e6..f82383d279e 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -1249,16 +1249,21 @@ private static List> updatePropertyValidators(DomainProperty PropertyDescriptor oldPropertyDescriptor = dp.getPropertyDescriptor().clone(); String oldValidatorStr = dp.getPropertyValidatorStringVal(); // record the old value before dp is mutated + boolean hasChange = false; for (GWTPropertyValidator v : newPd.getPropertyValidators()) { if (v.getRowId() != 0) + { + hasChange = true; newProps.put(v.getRowId(), v); + } else { Lsid lsid = DefaultPropertyValidator.createValidatorURI(v.getType()); IPropertyValidator pv = PropertyService.get().createValidator(lsid.toString()); - _copyValidator(pv, v); + boolean change = _copyValidator(pv, v); + hasChange = hasChange || change; dp.addValidator(pv); } @@ -1278,19 +1283,26 @@ private static List> updatePropertyValidators(DomainProperty if (v.equals(prop)) newProps.remove(v.getRowId()); else if (prop == null) + { deleted.add(v); + hasChange = true; + } } // update any new or changed for (IPropertyValidator pv : dp.getValidators()) - _copyValidator(pv, newProps.get(pv.getRowId())); + { + boolean change = _copyValidator(pv, newProps.get(pv.getRowId())); + hasChange = hasChange || change; + } // deal with removed validators for (GWTPropertyValidator gpv : deleted) dp.removeValidator(gpv.getRowId()); } - dp.checkValidatorEdit(oldValidatorStr, oldPropertyDescriptor); // mark dirty as needed + if (hasChange) + dp.setOldPropertyDescriptor(oldPropertyDescriptor); // mark dirty as needed return oldPd != null ? valueUpdates : null; } @@ -1360,20 +1372,35 @@ private static void updateTextChoiceValueRows(Domain domain, User user, String p } } - private static void _copyValidator(IPropertyValidator pv, GWTPropertyValidator gpv) + /** + * + * @param pv + * @param gpv + * @return true if has change. + */ + private static boolean _copyValidator(IPropertyValidator pv, GWTPropertyValidator gpv) { if (pv != null && gpv != null) { + StringBuilder changeDetails = new StringBuilder(); + changeDetails.append(DomainUtil.getStringPropChangeMsg("Name", pv.getName(), gpv.getName())); pv.setName(gpv.getName()); + changeDetails.append(DomainUtil.getStringPropChangeMsg("Description", pv.getDescription(), gpv.getDescription())); pv.setDescription(gpv.getDescription()); + changeDetails.append(DomainUtil.getStringPropChangeMsg("Expression", pv.getExpressionValue(), gpv.getExpression())); pv.setExpressionValue(gpv.getExpression()); + changeDetails.append(DomainUtil.getStringPropChangeMsg("ErrorMsg", pv.getErrorMessage(), gpv.getErrorMessage())); pv.setErrorMessage(gpv.getErrorMessage()); + changeDetails.append(DomainUtil.getStringPropChangeMsg("Properties", PageFlowUtil.toQueryString(pv.getProperties().entrySet()), PageFlowUtil.toQueryString(gpv.getProperties().entrySet()))); for (Map.Entry entry : gpv.getProperties().entrySet()) { pv.setProperty(entry.getKey(), entry.getValue()); } + return !changeDetails.toString().isEmpty(); } + + return false; } private static String getDomainErrorMessage(@Nullable GWTDomain domain, String message) diff --git a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java index 8b3aa090495..c967b4ab3c6 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java @@ -746,7 +746,7 @@ else if (impl.isNew()) if (isImplNew) propertyAuditInfo.add(new PropertyChangeAuditInfo(impl, true)); - else if (pdOld != null) + else if (null != pdOld) { PropertyChangeAuditInfo auditInfo = new PropertyChangeAuditInfo(impl, newPropName, pdOld, oldValidators, oldFormats); if (auditInfo.isChanged()) diff --git a/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java index 834aea8851e..fb25d093c02 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java @@ -650,14 +650,12 @@ public String getPropertyValidatorStringVal() } @Override - public void checkValidatorEdit(String oldValidatorStr, PropertyDescriptor oldPropertyDescriptor) + public void setOldPropertyDescriptor(PropertyDescriptor oldPropertyDescriptor) { if (isEdited()) return; - String newValidatorStr = DomainImpl.getPropertyValidatorStringVal(DomainPropertyManager.get().getValidators(this)); - if (!oldValidatorStr.equals(newValidatorStr)) - _pdOld = oldPropertyDescriptor.clone(); + _pdOld = oldPropertyDescriptor.clone(); } @Override diff --git a/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java b/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java index 30d38ad3fa7..8190626788c 100644 --- a/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java +++ b/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java @@ -152,9 +152,17 @@ public final PropertyValidator clone() } } + public String getPropertyAuditStr() + { + String propertyStr = getProperties(); + if ("failOnMatch=false".equals(propertyStr)) // ignore NULL vs false + return null; + return propertyStr; + } + public String getStringVal() { - return getName() + "; " + getExpression() + "; " + getProperties() + "; " + getErrorMessage() + "; " + getDescription() + "; " + + return getName() + ", " + getExpression() + ", " + getPropertyAuditStr() + ", " + getErrorMessage() + ", " + getDescription() + ", " + StringUtils.replace(PropertyService.get().getValidatorKind(getTypeURI()).getName(), " Property Validator", ""); } } \ No newline at end of file From 0dd74aa83c80fcaf48179fbec28985c6e05e052d Mon Sep 17 00:00:00 2001 From: XingY Date: Tue, 13 May 2025 09:18:12 -0700 Subject: [PATCH 06/30] fix dataset delete --- study/src/org/labkey/study/model/StudyManager.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/study/src/org/labkey/study/model/StudyManager.java b/study/src/org/labkey/study/model/StudyManager.java index f26b6c70185..0606848635e 100644 --- a/study/src/org/labkey/study/model/StudyManager.java +++ b/study/src/org/labkey/study/model/StudyManager.java @@ -2643,6 +2643,17 @@ private void deleteDatasetType(Study study, User user, DatasetDefinition ds, Str if (ds.getDomain() != null) ds.getDomain().delete(user, auditUserComment); + else if (ds.getTypeURI() != null) + { + try + { + OntologyManager.deleteType(ds.getTypeURI(), study.getContainer()); + } + catch (DomainNotFoundException x) + { + // continue + } + } } // Any container can be passed here (whether it contains a study or not). From b255592b400aeb839b2f6603edb85ce62b1ccc9a Mon Sep 17 00:00:00 2001 From: XingY Date: Tue, 13 May 2025 12:58:46 -0700 Subject: [PATCH 07/30] fix dataset delete --- .../org/labkey/api/exp/list/ListDefinition.java | 1 + .../labkey/list/model/ListDefinitionImpl.java | 8 +++++++- .../org/labkey/list/model/ListDomainKind.java | 2 +- .../org/labkey/study/model/StudyManager.java | 17 ++++++++++++++--- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/api/src/org/labkey/api/exp/list/ListDefinition.java b/api/src/org/labkey/api/exp/list/ListDefinition.java index 40e5a70b432..1ba5ea0e29e 100644 --- a/api/src/org/labkey/api/exp/list/ListDefinition.java +++ b/api/src/org/labkey/api/exp/list/ListDefinition.java @@ -292,6 +292,7 @@ public static BodySetting getForValue(int value) void save(User user) throws Exception; void save(User user, boolean ensureKey) throws Exception; void delete(User user) throws DomainNotFoundException; + void delete(User user, String auditUserComment) throws DomainNotFoundException; ListItem createListItem(); ListItem getListItem(Object key, User user); diff --git a/list/src/org/labkey/list/model/ListDefinitionImpl.java b/list/src/org/labkey/list/model/ListDefinitionImpl.java index 29398ba717a..9881df3f6c4 100644 --- a/list/src/org/labkey/list/model/ListDefinitionImpl.java +++ b/list/src/org/labkey/list/model/ListDefinitionImpl.java @@ -525,6 +525,12 @@ private boolean hasListItem(SimpleFilter filter, User user, Container c) @Override public void delete(User user) throws DomainNotFoundException + { + delete(user, null); + } + + @Override + public void delete(User user, String auditUserComment) throws DomainNotFoundException { TableInfo table = getTable(user); QueryUpdateService qus = null; @@ -545,7 +551,7 @@ public void delete(User user) throws DomainNotFoundException // then delete the list itself ListManager.get().deleteListDef(getContainer(), getListId()); Domain domain = getDomain(); - domain.delete(user); + domain.delete(user, auditUserComment); ListManager.get().addAuditEvent(this, user, String.format("The list %s was deleted", _def.getName())); diff --git a/list/src/org/labkey/list/model/ListDomainKind.java b/list/src/org/labkey/list/model/ListDomainKind.java index 89678239c23..df716ba836f 100644 --- a/list/src/org/labkey/list/model/ListDomainKind.java +++ b/list/src/org/labkey/list/model/ListDomainKind.java @@ -712,7 +712,7 @@ public void deleteDomain(User user, Domain domain, String userComment) try { - list.delete(user); // + list.delete(user, userComment); } catch (DomainNotFoundException e) { diff --git a/study/src/org/labkey/study/model/StudyManager.java b/study/src/org/labkey/study/model/StudyManager.java index 0606848635e..07f3730d7f9 100644 --- a/study/src/org/labkey/study/model/StudyManager.java +++ b/study/src/org/labkey/study/model/StudyManager.java @@ -97,6 +97,7 @@ import org.labkey.api.exp.api.ProvenanceService; import org.labkey.api.exp.api.StorageProvisioner; import org.labkey.api.exp.property.Domain; +import org.labkey.api.exp.property.DomainAuditProvider; import org.labkey.api.exp.property.DomainProperty; import org.labkey.api.exp.property.PropertyService; import org.labkey.api.exp.property.SystemProperty; @@ -2641,9 +2642,19 @@ private void deleteDatasetType(Study study, User user, DatasetDefinition ds, Str if (!ds.canDeleteDefinition(user)) throw new IllegalStateException("Can't delete dataset: " + ds.getName()); - if (ds.getDomain() != null) - ds.getDomain().delete(user, auditUserComment); - else if (ds.getTypeURI() != null) + Domain domain = ds.getDomain(); + if (domain == null) + return; + + DomainAuditProvider.DomainAuditEvent event = new DomainAuditProvider.DomainAuditEvent(study.getContainer(), String.format("The domain %s was deleted", domain.getName())); + event.setUserComment(auditUserComment); + event.setDomainUri(domain.getTypeURI()); + event.setDomainName(domain.getName()); + AuditLogService.get().addEvent(user, event); + + StorageProvisioner.get().drop(domain); + + if (ds.getTypeURI() != null) { try { From 850e993d864474a8995d93021e1936422f561f7d Mon Sep 17 00:00:00 2001 From: XingY Date: Tue, 13 May 2025 14:46:59 -0700 Subject: [PATCH 08/30] update tests --- .../src/org/labkey/experiment/api/property/DomainImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java index c967b4ab3c6..261e6b84e88 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java @@ -817,7 +817,7 @@ else if (null != pdOld) } final boolean finalPropChanged = propChanged; - final String extraAuditComment = auditComment == null ? "" : auditComment + ' '; + final String extraAuditComment = StringUtils.isEmpty(auditComment) ? "" : auditComment + ' '; // Move audit event creation to outside the transaction to avoid deadlocks involving audit storage table creation Runnable afterDomainCommit = () -> From 987dbcd0e87fdb891a4420da3f0b21c250a9b876 Mon Sep 17 00:00:00 2001 From: XingY Date: Tue, 13 May 2025 19:47:38 -0700 Subject: [PATCH 09/30] fix folder exclusion audit --- .../labkey/api/exp/api/ExperimentService.java | 2 +- .../labkey/assay/AssayDomainServiceImpl.java | 10 +++++----- .../experiment/api/ExperimentServiceImpl.java | 19 +++++++++++++------ .../experiment/api/SampleTypeServiceImpl.java | 13 +++++++++---- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/api/src/org/labkey/api/exp/api/ExperimentService.java b/api/src/org/labkey/api/exp/api/ExperimentService.java index 2ccdfde25da..e6e5a2ef225 100644 --- a/api/src/org/labkey/api/exp/api/ExperimentService.java +++ b/api/src/org/labkey/api/exp/api/ExperimentService.java @@ -1055,7 +1055,7 @@ List getExpProtocolsWithParameterValue( void ensureContainerDataTypeExclusions(@NotNull DataTypeForExclusion dataType, @Nullable DataTypeForExclusion relatedDataType, @Nullable Collection excludedDataTypeRowIds, @NotNull String excludedContainerId, User user); - void ensureDataTypeContainerExclusions(@NotNull DataTypeForExclusion dataType, @Nullable Collection excludedContainerIds, @NotNull Integer dataTypeId, User user); + @NotNull String ensureDataTypeContainerExclusions(@NotNull DataTypeForExclusion dataType, @Nullable Collection excludedContainerIds, @NotNull Integer dataTypeId, User user); void ensureDataTypeContainerExclusionsNonAdmin(@NotNull DataTypeForExclusion dataType, @NotNull Integer dataTypeId, Container container, User user); diff --git a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java index 9325fbbffcf..25a376beeaf 100644 --- a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java +++ b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java @@ -627,6 +627,11 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr protocol.save(getUser()); + if (assay.getExcludedContainerIds() != null && (!isNew || !assay.getExcludedContainerIds().isEmpty())) + changeDetails.append(ExperimentService.get().ensureDataTypeContainerExclusions(ExperimentService.DataTypeForExclusion.AssayDesign, assay.getExcludedContainerIds(), protocol.getRowId(), getUser())); + else + ExperimentService.get().ensureDataTypeContainerExclusionsNonAdmin(ExperimentService.DataTypeForExclusion.AssayDesign, protocol.getRowId(), getContainer(), getUser()); + for (GWTDomain domain : assay.getDomains()) { GWTDomain previous = DomainUtil.getDomainDescriptor(getUser(), domain.getDomainURI(), protocol.getContainer()); @@ -637,11 +642,6 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr QueryService.get().saveCalculatedFieldsMetadata(savedDomain.getSchemaName(), savedDomain.getQueryName(), null, domain.getCalculatedFields(), hasExistingCalcFields, getUser(), protocol.getContainer()); } - if (assay.getExcludedContainerIds() != null && (!isNew || !assay.getExcludedContainerIds().isEmpty())) - ExperimentService.get().ensureDataTypeContainerExclusions(ExperimentService.DataTypeForExclusion.AssayDesign, assay.getExcludedContainerIds(), protocol.getRowId(), getUser()); - else - ExperimentService.get().ensureDataTypeContainerExclusionsNonAdmin(ExperimentService.DataTypeForExclusion.AssayDesign, protocol.getRowId(), getContainer(), getUser()); - QueryService.get().updateLastModified(); transaction.commit(); AssayManager.get().clearProtocolCache(); diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index 5da569d9bd2..2e0809b2e73 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -8040,6 +8040,9 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u if (hasNameChange) QueryChangeListener.QueryPropertyChange.handleQueryNameChange(oldDataClassName, newName, schemaKey, u, c); + if (options != null && options.getExcludedContainerIds() != null) + changeDetails.append(ExperimentService.get().ensureDataTypeContainerExclusions(DataTypeForExclusion.DataClass, options.getExcludedContainerIds(), dataClass.getRowId(), u)); + errors = DomainUtil.updateDomainDescriptor(original, update, c, u, hasNameChange, changeDetails.toString(), auditUserComment); QueryService.get().saveCalculatedFieldsMetadata(schemaKey.toString(), update.getQueryName(), hasNameChange ? newName : null, update.getCalculatedFields(), !original.getCalculatedFields().isEmpty(), u, c); @@ -8047,9 +8050,6 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u if (hasNameChange) addObjectLegacyName(dataClass.getObjectId(), ExperimentServiceImpl.getNamespacePrefix(ExpDataClass.class), oldDataClassName, u); - if (options != null && options.getExcludedContainerIds() != null) - ExperimentService.get().ensureDataTypeContainerExclusions(DataTypeForExclusion.DataClass, options.getExcludedContainerIds(), dataClass.getRowId(), u); - if (!errors.hasErrors()) { transaction.addCommitTask(() -> clearDataClassCache(c), DbScope.CommitTaskOption.IMMEDIATE, POSTCOMMIT, POSTROLLBACK); @@ -8874,12 +8874,14 @@ public void ensureContainerDataTypeExclusions(@NotNull DataTypeForExclusion data } @Override - public void ensureDataTypeContainerExclusions(@NotNull DataTypeForExclusion dataType, @Nullable Collection excludedContainerIds, @NotNull Integer dataTypeId, User user) + @NotNull + public String ensureDataTypeContainerExclusions(@NotNull DataTypeForExclusion dataType, @Nullable Collection excludedContainerIds, @NotNull Integer dataTypeId, User user) { + Set previousExclusions = getDataTypeContainerExclusions(dataType, dataTypeId); + if (excludedContainerIds == null) - return; + return DomainUtil.getCollectionPropChangeMsg("ContainerExclusions", previousExclusions, null); - Set previousExclusions = getDataTypeContainerExclusions(dataType, dataTypeId); Set updatedExclusions = new HashSet<>(excludedContainerIds); Set toAdd = new HashSet<>(updatedExclusions); @@ -8905,6 +8907,11 @@ public void ensureDataTypeContainerExclusions(@NotNull DataTypeForExclusion data addAuditEventForDataTypeContainerUpdate(dataType, remove, user); } } + + if (!toAdd.isEmpty() || !toRemove.isEmpty()) + return DomainUtil.getCollectionPropChangeMsg("ContainerExclusions", previousExclusions, updatedExclusions); + + return ""; } @Override diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index 0f6ada63a5b..811d37cdc48 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -1104,6 +1104,15 @@ public ValidationException updateSampleType(GWTDomain { From 43cc1743305bdba3b6741f39b706bcfea87befca Mon Sep 17 00:00:00 2001 From: XingY Date: Tue, 13 May 2025 23:38:13 -0700 Subject: [PATCH 10/30] fix audit detail for field format change --- .../src/org/labkey/experiment/api/property/DomainImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java index 261e6b84e88..93b67c7fcf8 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java @@ -1078,7 +1078,7 @@ private String makeModifiedPropAuditComment(DomainPropertyImpl prop, String newP if (!StringUtils.equals(pdOld.getDescription(), prop.getDescription())) str.append("Description: ").append(renderOldVsNew(renderCheckingBlank(pdOld.getDescription()), renderCheckingBlank(prop.getDescription()))).append("; "); - if (!StringUtils.equals(prop.getFormat(), prop.getFormat())) + if (!StringUtils.equals(pdOld.getFormat(), prop.getFormat())) str.append("Format: ").append(renderOldVsNew(renderCheckingBlank(pdOld.getFormat()), renderCheckingBlank(prop.getFormat()))).append("; "); if (!StringUtils.equals((null != pdOld.getURL() ? pdOld.getURL().toString() : null), prop.getURL())) str.append("URL: ").append(renderOldVsNew(renderCheckingBlank(null != pdOld.getURL() ? pdOld.getURL().toString() : null), renderCheckingBlank(prop.getURL()))).append("; "); From cf08a841180efac9a8225bd9af7c4ece3526f708 Mon Sep 17 00:00:00 2001 From: XingY Date: Tue, 13 May 2025 23:38:32 -0700 Subject: [PATCH 11/30] selenium tests --- list/src/org/labkey/list/model/ListDomainKind.java | 2 +- .../labkey/test/tests/study/StudyDatasetsTest.java | 11 +++++++++++ .../src/org/labkey/test/tests/study/StudyTest.java | 7 +++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/list/src/org/labkey/list/model/ListDomainKind.java b/list/src/org/labkey/list/model/ListDomainKind.java index df716ba836f..c44b71bdd20 100644 --- a/list/src/org/labkey/list/model/ListDomainKind.java +++ b/list/src/org/labkey/list/model/ListDomainKind.java @@ -632,7 +632,7 @@ private Pair updateListProperties(ListDomainKi if (null != newListProps.getName()) { updatedListProps.setName(newListProps.getName().trim()); - changeDetails.append(DomainUtil.getPropChangeMsg("Name", existingListProps.getName(), newListProps.getName())); + // skip changeDetails for Name since it was previously explicitly added } changeDetails.append(DomainUtil.getPropChangeMsg("TitleColumn", existingListProps.getTitleColumn(), newListProps.getTitleColumn())); diff --git a/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java b/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java index 657963ad8f4..abcd0a3ac58 100644 --- a/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java +++ b/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java @@ -33,6 +33,7 @@ import org.labkey.test.pages.ImportDataPage; import org.labkey.test.pages.TimeChartWizard; import org.labkey.test.pages.study.DatasetDesignerPage; +import org.labkey.test.util.AuditLogHelper; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.LogMethod; import org.labkey.test.util.LoggedParam; @@ -82,6 +83,7 @@ public class StudyDatasetsTest extends BaseWebDriverTest a6\t6\tx6\ty6\tz6 """; private static final String DATASET_B_MERGE = "a4\t4\tx4_merged\ty4_merged\tz4_merged\n"; + private final AuditLogHelper _auditLogHelper = new AuditLogHelper(this); @Override protected BrowserType bestBrowser() @@ -250,7 +252,14 @@ protected void renameDataset(@Nullable String error, String orgName, String newN } if (error == null) + { editDatasetPage.clickSave(); + checker().verifyEquals("The comment logged for the list update was not as expected.", + "The name of the dataset '" + orgName + "' was changed to '" + newName.trim() + "'." + + "Label: " + orgLabel + " -> " + newLabel.trim() + "; The descriptor of domain " + orgName + " was updated", + _auditLogHelper.getLastDomainEventComment(getProjectName(), orgName)); + + } else { editDatasetPage.saveExpectFail(error); @@ -309,6 +318,8 @@ protected void deleteFields(String name) assertEquals(Arrays.asList("XTest"), remainingFields); editDatasetPage.clickSave(); + checker().verifyEquals("Domain audit comment not as expected after removing fields", "The column(s) of domain " + name + " were modified", _auditLogHelper.getLastDomainEventComment(getProjectName(), name)); + checker().verifyEqualsSorted("Domain field audit comment not as expected after removing fields", List.of("Deleted", "Deleted"), _auditLogHelper.getLastDomainPropertyValues(getProjectName(), name, "Action")); } @LogMethod diff --git a/study/test/src/org/labkey/test/tests/study/StudyTest.java b/study/test/src/org/labkey/test/tests/study/StudyTest.java index 9393eaa6b46..b212b9f77ac 100644 --- a/study/test/src/org/labkey/test/tests/study/StudyTest.java +++ b/study/test/src/org/labkey/test/tests/study/StudyTest.java @@ -78,6 +78,7 @@ public class StudyTest extends StudyBaseTest protected String datasetLink = datasetCount + " datasets"; protected static final String DEMOGRAPHICS_DESCRIPTION = "This is the demographics dataset, dammit. Here are some \u2018special symbols\u2019 - they help test that we're roundtripping in UTF-8."; protected static final String DEMOGRAPHICS_TITLE = "DEM-1: Demographics"; + protected static final String DEMOGRAPHICS_DOMAIN_NAME = "DEM-1"; protected String _tsv = "participantid\tsequencenum\tvisitdate\tSampleId\tDateField\tNumberField\tTextField\treplace\taliasedColumn\n" + "999321234\t1\t1/1/2006\t1234_A\t2/1/2006\t1.2\ttext\t\taliasedData\n" + @@ -1134,6 +1135,8 @@ protected void verifyManageDatasetsPage() setDemographicsBit(DEMOGRAPHICS_TITLE, false) .clickViewData(); + checker().verifyEquals("Domain audit comment not as expected after changing demographic bit", "IsDemographicData: true -> false; The descriptor of domain DEM-1 was updated", _auditLogHelper.getLastDomainEventComment(getProjectName(), DEMOGRAPHICS_DOMAIN_NAME)); + log("verify "); _customizeViewsHelper.openCustomizeViewPanel(); _customizeViewsHelper.showHiddenItems(); @@ -1292,6 +1295,10 @@ protected void verifyParticipantVisitDay() .selectVisitDateColumn("DEMdt") .clickApply() .clickSave(); + + checker().verifyEquals("Domain audit comment not as expected after changing visit date column", "VisitDateColumnName: -> DEMdt; The column(s) of domain DEM-1 were modified", _auditLogHelper.getLastDomainEventComment(getProjectName(), DEMOGRAPHICS_DOMAIN_NAME)); + checker().verifyEquals("Domain field audit comment not as expected", List.of("VisitDay"), _auditLogHelper.getLastDomainPropertyValues(getProjectName(), DEMOGRAPHICS_DOMAIN_NAME, "PropertyName")); + new DatasetPropertiesPage(getDriver()) .clickViewData(); From 24db6805c0d852dedbbc52b451f6ae7876c9a423 Mon Sep 17 00:00:00 2001 From: XingY Date: Wed, 14 May 2025 13:56:03 -0700 Subject: [PATCH 12/30] audit unique indices update --- .../labkey/api/gwt/client/model/GWTIndex.java | 55 ++++++++++++++----- .../labkey/api/exp/property/DomainUtil.java | 14 ++++- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java index 3b829d5a374..20401bfbc2a 100644 --- a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java +++ b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java @@ -1,21 +1,24 @@ -/* - * Copyright (c) 2018-2019 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* + * Copyright (c) 2018-2019 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.labkey.api.gwt.client.model; import com.google.gwt.user.client.rpc.IsSerializable; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.exp.property.DomainUtil; import java.io.Serializable; import java.util.ArrayList; @@ -69,4 +72,26 @@ public void setUnique(boolean unique) { _unique = unique; } + + public String toStringVal() + { + if (_columnNames == null || _columnNames.isEmpty()) + return ""; + + return StringUtils.join(_columnNames, ", ") + ", unique: " + isUnique(); + } + + public static List toStringVals(List indices) + { + if (indices == null || indices.isEmpty()) + return null; + + return indices.stream().map(GWTIndex::toStringVal).toList(); + } + + public static String getChangeMsg(@Nullable List oldIndices, List newIndices) + { + return DomainUtil.getCollectionPropChangeMsg("Index", toStringVals(oldIndices), toStringVals(newIndices)); + } + } diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index f82383d279e..97be1accc76 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -792,7 +792,15 @@ public static String getCollectionPropChangeMsg(String propertyName, Collect if (oldProp == null) return getPropChangeMsg(propertyName, null, StringUtils.join(newProp)); - return getPropChangeMsg(propertyName, StringUtils.join(oldProp), StringUtils.join(newProp)); + String oldStr = oldProp + .stream() + .map(Object::toString) + .sorted().collect(Collectors.joining(", ")); + String newStr = newProp + .stream() + .map(Object::toString) + .sorted().collect(Collectors.joining(", ")); + return getPropChangeMsg(propertyName, oldStr, newStr); } /** @return Errors encountered during the save attempt */ @@ -844,6 +852,8 @@ public static ValidationException updateDomainDescriptor(GWTDomain(defaultValues); From 95af4ec94c1401eba4320bddac8ece171ff364b0 Mon Sep 17 00:00:00 2001 From: XingY Date: Wed, 14 May 2025 15:10:32 -0700 Subject: [PATCH 13/30] code review changes --- .../api/audit/query/AbstractAuditDomainKind.java | 4 ++-- api/src/org/labkey/api/exp/api/ExpObject.java | 2 +- .../org/labkey/api/exp/api/ExperimentService.java | 5 ++++- .../labkey/api/exp/api/SampleTypeDomainKind.java | 4 ++-- .../org/labkey/api/exp/api/SampleTypeService.java | 2 +- .../org/labkey/api/exp/list/ListDefinition.java | 2 +- .../api/exp/property/AbstractDomainKind.java | 4 ++-- .../org/labkey/api/exp/property/DomainKind.java | 4 ++-- .../org/labkey/api/exp/property/DomainUtil.java | 7 ------- .../labkey/api/exp/property/TestDomainKind.java | 4 ++-- .../issues/AbstractIssuesListDefDomainKind.java | 4 ++-- api/src/org/labkey/api/study/Dataset.java | 2 +- .../assay/PlateBasedAssaySampleTypeDomainKind.java | 2 +- .../org/labkey/experiment/ExpDataFileListener.java | 2 +- .../labkey/experiment/api/DataClassDomainKind.java | 2 +- .../labkey/experiment/api/ExpSampleTypeImpl.java | 2 +- .../experiment/api/ExperimentServiceImpl.java | 14 +++++++------- .../experiment/api/SampleTypeServiceImpl.java | 14 +++++++------- .../experiment/api/VocabularyDomainKind.java | 2 +- .../controllers/property/PropertyController.java | 4 ++-- .../org/labkey/list/model/ListDefinitionImpl.java | 2 +- list/src/org/labkey/list/model/ListDomainKind.java | 2 +- .../specimen/model/AbstractSpecimenDomainKind.java | 2 +- .../api/specimen/model/SpecimenDomainKind.java | 2 +- .../specimen/model/SpecimenEventDomainKind.java | 2 +- .../labkey/api/specimen/model/VialDomainKind.java | 2 +- .../org/labkey/study/model/DatasetDefinition.java | 2 +- .../org/labkey/study/model/DatasetDomainKind.java | 2 +- study/src/org/labkey/study/model/StudyManager.java | 4 ++-- 29 files changed, 51 insertions(+), 55 deletions(-) diff --git a/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java b/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java index f66ce6778f1..7daba72dc54 100644 --- a/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java +++ b/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java @@ -248,13 +248,13 @@ public Domain createDomain(GWTDomain domain, JSONObject arguments, Container con @Override public ValidationException updateDomain(GWTDomain original, GWTDomain update, - JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) + JSONObject options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { throw new UnsupportedOperationException(); } @Override - public void deleteDomain(User user, Domain domain, String auditUserComment) + public void deleteDomain(User user, Domain domain, @Nullable String auditUserComment) { throw new UnsupportedOperationException(); } diff --git a/api/src/org/labkey/api/exp/api/ExpObject.java b/api/src/org/labkey/api/exp/api/ExpObject.java index 9b1f8dffce9..b7a862aa066 100644 --- a/api/src/org/labkey/api/exp/api/ExpObject.java +++ b/api/src/org/labkey/api/exp/api/ExpObject.java @@ -77,7 +77,7 @@ default void setComment(User user, String comment, boolean index) throws Validat void save(User user) throws BatchValidationException; void delete(User user); - default void delete(User user, String auditUserComment) + default void delete(User user, @Nullable String auditUserComment) { delete(user); } diff --git a/api/src/org/labkey/api/exp/api/ExperimentService.java b/api/src/org/labkey/api/exp/api/ExperimentService.java index e6e5a2ef225..5bcfba53739 100644 --- a/api/src/org/labkey/api/exp/api/ExperimentService.java +++ b/api/src/org/labkey/api/exp/api/ExperimentService.java @@ -280,7 +280,7 @@ ValidationException updateDataClass( @Nullable DataClassDomainKindProperties options, GWTDomain original, GWTDomain update, - String auditUserComment + @Nullable String auditUserComment ); /** @@ -1055,6 +1055,9 @@ List getExpProtocolsWithParameterValue( void ensureContainerDataTypeExclusions(@NotNull DataTypeForExclusion dataType, @Nullable DataTypeForExclusion relatedDataType, @Nullable Collection excludedDataTypeRowIds, @NotNull String excludedContainerId, User user); + /** + * @return Details about what's changed, to be used in audit log (old -> new) + */ @NotNull String ensureDataTypeContainerExclusions(@NotNull DataTypeForExclusion dataType, @Nullable Collection excludedContainerIds, @NotNull Integer dataTypeId, User user); void ensureDataTypeContainerExclusionsNonAdmin(@NotNull DataTypeForExclusion dataType, @NotNull Integer dataTypeId, Container container, User user); diff --git a/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java b/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java index 1b85d29d89b..f978bc1b581 100644 --- a/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java +++ b/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java @@ -394,7 +394,7 @@ public DefaultValueType[] getDefaultValueOptions(Domain domain) @Override @NotNull public ValidationException updateDomain(GWTDomain original, @NotNull GWTDomain update, - @Nullable SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, String auditUserComment) + @Nullable SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { return SampleTypeService.get().updateSampleType(original, update, options, container, user, includeWarnings, auditUserComment); } @@ -606,7 +606,7 @@ public Domain createDomain(GWTDomain domain, @Nullable SampleTypeDomainKindPrope } @Override - public void deleteDomain(User user, Domain domain, String auditUserComment) + public void deleteDomain(User user, Domain domain, @Nullable String auditUserComment) { ExpSampleType st = SampleTypeService.get().getSampleType(domain.getTypeURI()); if (st == null) diff --git a/api/src/org/labkey/api/exp/api/SampleTypeService.java b/api/src/org/labkey/api/exp/api/SampleTypeService.java index b38ebf178b4..9e2877249d8 100644 --- a/api/src/org/labkey/api/exp/api/SampleTypeService.java +++ b/api/src/org/labkey/api/exp/api/SampleTypeService.java @@ -230,7 +230,7 @@ default Map incrementSampleCounts(@Nullable Date counterDate) long getProjectRootSampleCount(Container container); - ValidationException updateSampleType(GWTDomain original, GWTDomain update, SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, String auditUserComment); + ValidationException updateSampleType(GWTDomain original, GWTDomain update, SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment); void addAuditEvent(User user, Container container, String comment, String userComment, ExpMaterial sample, Map metadata); diff --git a/api/src/org/labkey/api/exp/list/ListDefinition.java b/api/src/org/labkey/api/exp/list/ListDefinition.java index 1ba5ea0e29e..89ca4c58290 100644 --- a/api/src/org/labkey/api/exp/list/ListDefinition.java +++ b/api/src/org/labkey/api/exp/list/ListDefinition.java @@ -292,7 +292,7 @@ public static BodySetting getForValue(int value) void save(User user) throws Exception; void save(User user, boolean ensureKey) throws Exception; void delete(User user) throws DomainNotFoundException; - void delete(User user, String auditUserComment) throws DomainNotFoundException; + void delete(User user, @Nullable String auditUserComment) throws DomainNotFoundException; ListItem createListItem(); ListItem getListItem(Object key, User user); diff --git a/api/src/org/labkey/api/exp/property/AbstractDomainKind.java b/api/src/org/labkey/api/exp/property/AbstractDomainKind.java index 22c59f1a2cc..9b7178a7d56 100644 --- a/api/src/org/labkey/api/exp/property/AbstractDomainKind.java +++ b/api/src/org/labkey/api/exp/property/AbstractDomainKind.java @@ -123,13 +123,13 @@ public Domain createDomain(GWTDomain domain, T arguments, @Override @NotNull public ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable T options, Container container, User user, boolean includeWarnings, String auditUserComment) + @Nullable T options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { return DomainUtil.updateDomainDescriptor(original, update, container, user); } @Override - public void deleteDomain(User user, Domain domain, String auditUserComment) + public void deleteDomain(User user, Domain domain, @Nullable String auditUserComment) { } diff --git a/api/src/org/labkey/api/exp/property/DomainKind.java b/api/src/org/labkey/api/exp/property/DomainKind.java index fd3155ea977..8b96486c2d5 100644 --- a/api/src/org/labkey/api/exp/property/DomainKind.java +++ b/api/src/org/labkey/api/exp/property/DomainKind.java @@ -165,14 +165,14 @@ public Set getReservedPropertyNamePrefixes() * @return A list of errors collected during the update. */ abstract public ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable T options, Container container, User user, boolean includeWarnings, String auditUserComment); + @Nullable T options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment); /** * Delete a Domain and its associated data. * @param domain The domain to delete * @param user * @param auditUserComment */ - abstract public void deleteDomain(User user, Domain domain, String auditUserComment); + abstract public void deleteDomain(User user, Domain domain, @Nullable String auditUserComment); /** * Get base properties defined for that domainkind. The domain parameter is only when there may be a condition diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index 97be1accc76..ef80b3be488 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -1258,7 +1258,6 @@ private static List> updatePropertyValidators(DomainProperty List> valueUpdates = new ArrayList<>(); PropertyDescriptor oldPropertyDescriptor = dp.getPropertyDescriptor().clone(); - String oldValidatorStr = dp.getPropertyValidatorStringVal(); // record the old value before dp is mutated boolean hasChange = false; for (GWTPropertyValidator v : newPd.getPropertyValidators()) { @@ -1382,12 +1381,6 @@ private static void updateTextChoiceValueRows(Domain domain, User user, String p } } - /** - * - * @param pv - * @param gpv - * @return true if has change. - */ private static boolean _copyValidator(IPropertyValidator pv, GWTPropertyValidator gpv) { if (pv != null && gpv != null) diff --git a/api/src/org/labkey/api/exp/property/TestDomainKind.java b/api/src/org/labkey/api/exp/property/TestDomainKind.java index 58e257123f1..47fbf4aeacd 100644 --- a/api/src/org/labkey/api/exp/property/TestDomainKind.java +++ b/api/src/org/labkey/api/exp/property/TestDomainKind.java @@ -172,13 +172,13 @@ public Domain createDomain(GWTDomain domain, JSONObject arguments, Container con @Override public ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) + @Nullable JSONObject options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { throw new UnsupportedOperationException(); } @Override - public void deleteDomain(User user, Domain domain, String auditUserComment) + public void deleteDomain(User user, Domain domain, @Nullable String auditUserComment) { throw new UnsupportedOperationException(); } diff --git a/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java b/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java index b299c893f7f..96ff421afb4 100644 --- a/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java +++ b/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java @@ -212,7 +212,7 @@ public static String getDomainURI(String schemaName, String tableName, String na public abstract void beforeDeleteDomain(User user, Domain domain); @Override - public final void deleteDomain(User user, Domain domain, String auditUserComment) + public final void deleteDomain(User user, Domain domain, @Nullable String auditUserComment) { try (DbScope.Transaction transaction = IssuesSchema.getInstance().getSchema().getScope().ensureTransaction()) { @@ -288,7 +288,7 @@ public Class getTypeClass() @Override public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable IssuesDomainKindProperties options, Container container, User user, boolean includeWarnings, String auditUserComment) + @Nullable IssuesDomainKindProperties options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { if (options != null && StringUtils.isBlank(options.getIssueDefName())) return new ValidationException("Issue name must not be null."); diff --git a/api/src/org/labkey/api/study/Dataset.java b/api/src/org/labkey/api/study/Dataset.java index 555458ea47d..cd13190e51d 100644 --- a/api/src/org/labkey/api/study/Dataset.java +++ b/api/src/org/labkey/api/study/Dataset.java @@ -364,7 +364,7 @@ public String getRecallFromStudyAuditMessage(String label, int recordCount) void delete(User user); - void delete(User user, String auditUserComment); + void delete(User user, @Nullable String auditUserComment); void deleteAllRows(User user); diff --git a/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java b/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java index 0db9eefc9d6..74b22a0b62f 100644 --- a/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java +++ b/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java @@ -157,7 +157,7 @@ public Domain createDomain(GWTDomain domain, SampleTypeDomainKindProperties argu @NotNull @Override - public ValidationException updateDomain(GWTDomain original, GWTDomain update, @Nullable SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, String auditUserComment) + public ValidationException updateDomain(GWTDomain original, GWTDomain update, @Nullable SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { JSONObject args = options != null ? options.toJSONObject() : null; return _assayDelegate.updateDomain(original, update, args, container, user, includeWarnings, auditUserComment); diff --git a/experiment/src/org/labkey/experiment/ExpDataFileListener.java b/experiment/src/org/labkey/experiment/ExpDataFileListener.java index 76e3ceab767..76f6374869a 100644 --- a/experiment/src/org/labkey/experiment/ExpDataFileListener.java +++ b/experiment/src/org/labkey/experiment/ExpDataFileListener.java @@ -76,7 +76,7 @@ public int fileMoved(@NotNull Path src, @NotNull Path dest, @Nullable User user, // if the data object moved containers, set that as well if (targetContainer != null && !targetContainer.equals(sourceContainer)) data.setContainer(targetContainer); - data.save(user); // bad!! + data.save(user); // FIXME: Issue 53070: Moving files could result in duplicate exp.data records extra = 1; } diff --git a/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java b/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java index d9cba286ab3..05e50c23ba0 100644 --- a/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java +++ b/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java @@ -413,7 +413,7 @@ public Domain createDomain(GWTDomain domain, DataClassDom } @Override - public void deleteDomain(User user, Domain domain, String auditUserComment) + public void deleteDomain(User user, Domain domain, @Nullable String auditUserComment) { ExpDataClass dc = getDataClass(domain); if (dc == null) diff --git a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java index 13dedf46472..515ebde411a 100644 --- a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java @@ -921,7 +921,7 @@ public void delete(User user) } @Override - public void delete(User user, String auditUserComment) + public void delete(User user, @Nullable String auditUserComment) { try { diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index 2e0809b2e73..f062fa6d0d5 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -4510,7 +4510,7 @@ public List getExpRunsForProtocolIds(boolean includeRelated, @NotNul return ExpRunImpl.fromRuns(new SqlSelector(getExpSchema(), sb).getArrayList(ExperimentRun.class)); } - public void deleteProtocolByRowIds(Container c, User user, String auditUserComment, int... selectedProtocolIds) throws ExperimentException + public void deleteProtocolByRowIds(Container c, User user, @Nullable String auditUserComment, int... selectedProtocolIds) throws ExperimentException { if (selectedProtocolIds.length == 0) return; @@ -7970,7 +7970,7 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u @Nullable DataClassDomainKindProperties properties, GWTDomain original, GWTDomain update, - String auditUserComment) + @Nullable String auditUserComment) { ValidationException errors; StringBuilder changeDetails = new StringBuilder(); @@ -8014,18 +8014,18 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u throw new RuntimeException(e); } } - String oldImportAliasJson = null; + String oldImportAliasStr = null; try { - oldImportAliasJson = ExperimentJSONConverter.getImportAliasStringVal(dataClass.getImportAliasMap()); + oldImportAliasStr = ExperimentJSONConverter.getImportAliasStringVal(dataClass.getImportAliasMap()); } catch (IOException e) { throw new RuntimeException(e); } dataClass.setImportAliasMap(newAliases); - String newImportAliasJson = ExperimentJSONConverter.getImportAliasStringVal(newAliases); - changeDetails.append(DomainUtil.getStringPropChangeMsg("ImportAlias", oldImportAliasJson, newImportAliasJson)); + String newImportAliasStr = ExperimentJSONConverter.getImportAliasStringVal(newAliases); + changeDetails.append(DomainUtil.getStringPropChangeMsg("ImportAlias", oldImportAliasStr, newImportAliasStr)); if (!NameExpressionOptionService.get().allowUserSpecifiedNames(c) && options.getNameExpression() == null) throw new ApiUsageException(c.hasProductFolders() ? NAME_EXPRESSION_REQUIRED_MSG_WITH_SUBFOLDERS : NAME_EXPRESSION_REQUIRED_MSG); @@ -9613,7 +9613,7 @@ public Map moveDataClassObjects(Collection d return updateCounts; } - private void addDataClassSummaryAuditEvent(User user, Container container, TableInfo dataClassTable, int rowCount, String auditUserComment) + private void addDataClassSummaryAuditEvent(User user, Container container, TableInfo dataClassTable, int rowCount, @Nullable String auditUserComment) { QueryService queryService = QueryService.get(); queryService.getDefaultAuditHandler().addSummaryAuditEvent(user, container, dataClassTable, QueryService.AuditAction.UPDATE, rowCount, AuditBehaviorType.SUMMARY, auditUserComment); diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index 811d37cdc48..0e87d71edf8 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -646,12 +646,12 @@ public void deleteSampleType(int rowId, Container c, User user, @Nullable String LOG.info("Deleted SampleType '" + source.getName() + "' from '" + c.getPath() + "' in " + timer.getDuration()); } - private void addSampleTypeDeletedAuditEvent(User user, Container c, ExpSampleType sampleType, Long txAuditId, String auditUserComment) + private void addSampleTypeDeletedAuditEvent(User user, Container c, ExpSampleType sampleType, Long txAuditId, @Nullable String auditUserComment) { addSampleTypeAuditEvent(user, c, sampleType, txAuditId, String.format("Sample Type deleted: %1$s", sampleType.getName()),auditUserComment, "delete type"); } - private void addSampleTypeAuditEvent(User user, Container c, ExpSampleType sampleType, Long txAuditId, String comment, String auditUserComment, String insertUpdateChoice) + private void addSampleTypeAuditEvent(User user, Container c, ExpSampleType sampleType, Long txAuditId, String comment, @Nullable String auditUserComment, String insertUpdateChoice) { SampleTypeAuditProvider.SampleTypeAuditEvent event = new SampleTypeAuditProvider.SampleTypeAuditEvent(c, comment); event.setUserComment(auditUserComment); @@ -999,7 +999,7 @@ private void validateSampleTypeName(Container container, User user, String name, } @Override - public ValidationException updateSampleType(GWTDomain original, GWTDomain update, SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, String auditUserComment) + public ValidationException updateSampleType(GWTDomain original, GWTDomain update, SampleTypeDomainKindProperties options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { ValidationException errors; @@ -1077,18 +1077,18 @@ public ValidationException updateSampleType(GWTDomain original, GWTDomain update, - @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) + @Nullable JSONObject options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { DomainKind kind = PropertyService.get().getDomainKind(original.getDomainURI()); if (kind == null) @@ -1523,7 +1523,7 @@ private static ValidationException updateDomain(GWTDomain original, GWTDomain update, - ListDomainKindProperties listProperties, Container container, User user, boolean includeWarnings, String auditUserComment) + ListDomainKindProperties listProperties, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { ValidationException exception; diff --git a/study/api-src/org/labkey/api/specimen/model/AbstractSpecimenDomainKind.java b/study/api-src/org/labkey/api/specimen/model/AbstractSpecimenDomainKind.java index 518b5adb870..a20e2ed1e1a 100644 --- a/study/api-src/org/labkey/api/specimen/model/AbstractSpecimenDomainKind.java +++ b/study/api-src/org/labkey/api/specimen/model/AbstractSpecimenDomainKind.java @@ -88,7 +88,7 @@ public Domain createDomain(GWTDomain domain, JSONObject a } @Override - public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) + public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, @Nullable JSONObject options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { ValidationException validation = checkFieldNameLength(update); if (validation != null) return validation; diff --git a/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java b/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java index 866bb2d03e9..b54a91b3ce4 100644 --- a/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java +++ b/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java @@ -168,7 +168,7 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) @Override public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) + @Nullable JSONObject options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { ValidationException exception; try (var transaction = SpecimenSchema.get().getScope().ensureTransaction()) diff --git a/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java b/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java index 058fd62ba91..f2e341d06ae 100644 --- a/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java +++ b/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java @@ -233,7 +233,7 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) @Override public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) + @Nullable JSONObject options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { ValidationException validationException; try (var transaction = SpecimenSchema.get().getScope().ensureTransaction()) diff --git a/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java b/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java index 2ee468eca9f..39e311136b1 100644 --- a/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java +++ b/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java @@ -171,7 +171,7 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) @Override public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, - @Nullable JSONObject options, Container container, User user, boolean includeWarnings, String auditUserComment) + @Nullable JSONObject options, Container container, User user, boolean includeWarnings, @Nullable String auditUserComment) { ValidationException exception; try (var transaction = SpecimenSchema.get().getScope().ensureTransaction()) diff --git a/study/src/org/labkey/study/model/DatasetDefinition.java b/study/src/org/labkey/study/model/DatasetDefinition.java index 2532a12f797..1cdae2a08a9 100644 --- a/study/src/org/labkey/study/model/DatasetDefinition.java +++ b/study/src/org/labkey/study/model/DatasetDefinition.java @@ -1152,7 +1152,7 @@ public boolean hasMatchingExtraKey(Dataset other) } @Override - public void delete(User user, String auditUserComment) + public void delete(User user, @Nullable String auditUserComment) { if (!canDeleteDefinition(user)) { diff --git a/study/src/org/labkey/study/model/DatasetDomainKind.java b/study/src/org/labkey/study/model/DatasetDomainKind.java index bc2a6fc1636..28722715c2a 100644 --- a/study/src/org/labkey/study/model/DatasetDomainKind.java +++ b/study/src/org/labkey/study/model/DatasetDomainKind.java @@ -779,7 +779,7 @@ private ValidationException updateDataset(DatasetDomainKindProperties datasetPro } @Override - public void deleteDomain(User user, Domain domain, String auditUserComment) + public void deleteDomain(User user, Domain domain, @Nullable String auditUserComment) { DatasetDefinition def = StudyManager.getInstance().getDatasetDefinition(domain.getTypeURI()); if (def == null) diff --git a/study/src/org/labkey/study/model/StudyManager.java b/study/src/org/labkey/study/model/StudyManager.java index 07f3730d7f9..c4f89d49288 100644 --- a/study/src/org/labkey/study/model/StudyManager.java +++ b/study/src/org/labkey/study/model/StudyManager.java @@ -2550,7 +2550,7 @@ public int purgeDataset(DatasetDefinition dataset, @Nullable Date cutoff) * @param performStudyResync whether to kick off our normal bookkeeping. If the whole study is being deleted, * we don't need to bother doing this, for example. */ - public void deleteDataset(StudyImpl study, User user, DatasetDefinition ds, boolean performStudyResync, String auditUserComment) + public void deleteDataset(StudyImpl study, User user, DatasetDefinition ds, boolean performStudyResync, @Nullable String auditUserComment) { try (Transaction transaction = StudySchema.getInstance().getScope().ensureTransaction()) { @@ -2632,7 +2632,7 @@ public void deleteDataset(StudyImpl study, User user, DatasetDefinition ds, bool /** delete a dataset type and data * does not clear typeURI as we're about to delete the dataset */ - private void deleteDatasetType(Study study, User user, DatasetDefinition ds, String auditUserComment) throws DomainNotFoundException + private void deleteDatasetType(Study study, User user, DatasetDefinition ds, @Nullable String auditUserComment) { assert StudySchema.getInstance().getSchema().getScope().isTransactionActive(); From 2f33703d74157f18a1830f104b3cbd39096c0aee Mon Sep 17 00:00:00 2001 From: XingY Date: Thu, 15 May 2025 23:44:08 -0700 Subject: [PATCH 14/30] switch to use old and new record map for domain event details --- .../labkey/api/gwt/client/model/GWTIndex.java | 5 - .../api/audit/AbstractAuditTypeProvider.java | 3 +- .../labkey/api/data/ConditionalFormat.java | 4 +- .../labkey/api/exp/PropertyDescriptor.java | 53 ++++ .../api/DataClassDomainKindProperties.java | 20 ++ .../labkey/api/exp/api/ExperimentService.java | 2 +- .../api/SampleTypeDomainKindProperties.java | 27 ++ .../org/labkey/api/exp/property/Domain.java | 2 +- .../api/exp/property/DomainAuditProvider.java | 16 +- .../api/exp/property/DomainProperty.java | 1 - .../property/DomainPropertyAuditProvider.java | 15 +- .../labkey/api/exp/property/DomainUtil.java | 30 +- .../experiment/api/ExpDataClassImpl.java | 24 ++ .../experiment/api/ExpSampleTypeImpl.java | 34 +++ .../experiment/api/ExperimentServiceImpl.java | 47 ++- .../experiment/api/SampleTypeServiceImpl.java | 46 ++- .../experiment/api/property/DomainImpl.java | 278 +++++------------- .../api/property/DomainPropertyImpl.java | 62 +++- .../api/property/PropertyValidator.java | 13 +- .../api/property/PropertyValidatorImpl.java | 15 + .../org/labkey/list/model/ListDomainKind.java | 47 ++- .../list/model/ListDomainKindProperties.java | 34 +++ .../labkey/study/model/DatasetDefinition.java | 24 ++ .../labkey/study/model/DatasetDomainKind.java | 13 +- .../model/DatasetDomainKindProperties.java | 29 ++ 25 files changed, 513 insertions(+), 331 deletions(-) diff --git a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java index 20401bfbc2a..4c7e7eb9147 100644 --- a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java +++ b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java @@ -89,9 +89,4 @@ public static List toStringVals(List indices) return indices.stream().map(GWTIndex::toStringVal).toList(); } - public static String getChangeMsg(@Nullable List oldIndices, List newIndices) - { - return DomainUtil.getCollectionPropChangeMsg("Index", toStringVals(oldIndices), toStringVals(newIndices)); - } - } diff --git a/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java b/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java index b3a8e48c274..189cd73754f 100644 --- a/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java +++ b/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java @@ -16,6 +16,7 @@ package org.labkey.api.audit; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.labkey.api.audit.data.DataMapColumn; import org.labkey.api.audit.data.DataMapDiffColumn; import org.labkey.api.audit.query.AbstractAuditDomainKind; @@ -394,7 +395,7 @@ public static Map decodeFromDataMap(String properties) } } - public static String encodeForDataMap(Container c, Map properties) + public static String encodeForDataMap(@Nullable Container c, Map properties) { if (properties == null) return null; diff --git a/api/src/org/labkey/api/data/ConditionalFormat.java b/api/src/org/labkey/api/data/ConditionalFormat.java index b7623a49f6a..4a0c3a5faab 100644 --- a/api/src/org/labkey/api/data/ConditionalFormat.java +++ b/api/src/org/labkey/api/data/ConditionalFormat.java @@ -383,10 +383,10 @@ public String toStringVal() return getFilter() + ": " + getCssStyle(); } - public static String toStringVal(List formats) + public static @Nullable String toStringVal(List formats) { if (formats == null || formats.isEmpty()) - return ""; + return null; List strings = new ArrayList<>(); formats.forEach(format -> { diff --git a/api/src/org/labkey/api/exp/PropertyDescriptor.java b/api/src/org/labkey/api/exp/PropertyDescriptor.java index 6db6245f25b..990b8e172ff 100644 --- a/api/src/org/labkey/api/exp/PropertyDescriptor.java +++ b/api/src/org/labkey/api/exp/PropertyDescriptor.java @@ -15,6 +15,7 @@ */ package org.labkey.api.exp; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; @@ -48,6 +49,7 @@ import java.io.Serializable; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -537,6 +539,57 @@ public void setDatabaseDefaultValue(@Nullable Object databaseDefaultValue) { _databaseDefaultValue = databaseDefaultValue; } + + public Map getAuditRecordMap(@Nullable String validatorStr, @Nullable String conditionalFormatStr) + { + Map map = new LinkedHashMap<>(); + if (getName() != null) + map.put("Name", getName()); + if (getLabel() != null) + map.put("Label", getLabel()); + if (null != getPropertyType()) + map.put("Type", getPropertyType().getXarName()); + if (getPropertyType().getJdbcType().isText()) + map.put("Scale", getScale()); + if (getDescription() != null) + map.put("Description", getDescription()); + if (getFormat() != null) + map.put("Format", getFormat()); + if (getURL() != null) + map.put("URL", getURL().toString()); + if (getPHI() != null) + map.put("PHI", getPHI().getLabel()); + if (getDefaultScale() != null) + map.put("DefaultScale", getDefaultScale().getLabel()); + map.put("Required", isRequired()); + map.put("Hidden", isHidden()); + map.put("MvEnabled", isMvEnabled()); + map.put("Measure", isMeasure()); + map.put("Dimension", isDimension()); + map.put("ShownInInsert", isShownInInsertView()); + map.put("ShownInDetails", isShownInDetailsView()); + map.put("ShownInUpdate", isShownInUpdateView()); + map.put("ShownInLookupView", isShownInLookupView()); + map.put("RecommendedVariable", isRecommendedVariable()); + map.put("ExcludedFromShifting", isExcludeFromShifting()); + map.put("Scannable", isScannable()); + if (getDerivationDataScope() != null) + map.put("DerivationDataScope", getDerivationDataScope()); + String importAliasStr = StringUtils.join(getImportAliasSet(), ","); + if (!StringUtils.isEmpty(importAliasStr)) + map.put("ImportAliases", importAliasStr); + if (getDefaultValueTypeEnum() != null) + map.put("DefaultValueType", getDefaultValueTypeEnum().getLabel()); + if (getLookup() != null) + map.put("Lookup", getLookup().toJSONString()); + if (validatorStr != null) + map.put("Validator", validatorStr); + if (conditionalFormatStr != null) + map.put("ConditionalFormat", conditionalFormatStr); + + return map; + } + } diff --git a/api/src/org/labkey/api/exp/api/DataClassDomainKindProperties.java b/api/src/org/labkey/api/exp/api/DataClassDomainKindProperties.java index 545c99aad22..d0d43eb2828 100644 --- a/api/src/org/labkey/api/exp/api/DataClassDomainKindProperties.java +++ b/api/src/org/labkey/api/exp/api/DataClassDomainKindProperties.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -202,4 +203,23 @@ public void setExcludedContainerIds(List excludedContainerIds) { this.excludedContainerIds = excludedContainerIds; } + + public Map getAuditRecordMap() + { + Map map = new LinkedHashMap<>(); + // skip Name and Description since it's general domain property + if (!StringUtils.isEmpty(getNameExpression())) + map.put("nameExpression", getNameExpression()); + String importAliasStr = ExperimentJSONConverter.getImportAliasStringVal(getImportAliases()); + if (!StringUtils.isEmpty(importAliasStr)) + map.put("ImportAlias", importAliasStr); + if (!StringUtils.isEmpty(getCategory())) + map.put("Category", getCategory()); + if (getSampleType() != null) + map.put("SampleType", getSampleType()); + + return map; + } + + } diff --git a/api/src/org/labkey/api/exp/api/ExperimentService.java b/api/src/org/labkey/api/exp/api/ExperimentService.java index 5bcfba53739..db2d42348bf 100644 --- a/api/src/org/labkey/api/exp/api/ExperimentService.java +++ b/api/src/org/labkey/api/exp/api/ExperimentService.java @@ -1058,7 +1058,7 @@ List getExpProtocolsWithParameterValue( /** * @return Details about what's changed, to be used in audit log (old -> new) */ - @NotNull String ensureDataTypeContainerExclusions(@NotNull DataTypeForExclusion dataType, @Nullable Collection excludedContainerIds, @NotNull Integer dataTypeId, User user); + @NotNull Pair, Collection> ensureDataTypeContainerExclusions(@NotNull DataTypeForExclusion dataType, @Nullable Collection excludedContainerIds, @NotNull Integer dataTypeId, User user); void ensureDataTypeContainerExclusionsNonAdmin(@NotNull DataTypeForExclusion dataType, @NotNull Integer dataTypeId, Container container, User user); diff --git a/api/src/org/labkey/api/exp/api/SampleTypeDomainKindProperties.java b/api/src/org/labkey/api/exp/api/SampleTypeDomainKindProperties.java index 69eae7d7cf5..0aa8f370d3c 100644 --- a/api/src/org/labkey/api/exp/api/SampleTypeDomainKindProperties.java +++ b/api/src/org/labkey/api/exp/api/SampleTypeDomainKindProperties.java @@ -3,12 +3,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; import java.io.IOException; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -272,4 +274,29 @@ public void setExcludedDashboardContainerIds(List excludedDashboardConta { this.excludedDashboardContainerIds = excludedDashboardContainerIds; } + + public Map getAuditRecordMap() + { + Map map = new LinkedHashMap<>(); + // skip Name and Description since it's general domain property + if (!StringUtils.isEmpty(getNameExpression())) + map.put("nameExpression", getNameExpression()); + if (!StringUtils.isEmpty(getAliquotNameExpression())) + map.put("AliquotNameExpression", getAliquotNameExpression()); + if (!StringUtils.isEmpty(getLabelColor())) + map.put("LabelColor", getLabelColor()); + if (!StringUtils.isEmpty(getMetricUnit())) + map.put("MetricUnit", getMetricUnit()); + String importAliasStr = ExperimentJSONConverter.getImportAliasStringVal(getImportAliases()); + if (!StringUtils.isEmpty(importAliasStr)) + map.put("ImportAlias", importAliasStr); + if (!StringUtils.isEmpty(getAutoLinkTargetContainerId())) + map.put("AutoLinkTargetContainerId", getAutoLinkTargetContainerId()); + if (!StringUtils.isEmpty(getAutoLinkCategory())) + map.put("AutoLinkCategory", getAutoLinkCategory()); + if (!StringUtils.isEmpty(getCategory())) + map.put("Category", getCategory()); + + return map; + } } diff --git a/api/src/org/labkey/api/exp/property/Domain.java b/api/src/org/labkey/api/exp/property/Domain.java index 22f94fd99c2..9e62df3dbf5 100644 --- a/api/src/org/labkey/api/exp/property/Domain.java +++ b/api/src/org/labkey/api/exp/property/Domain.java @@ -89,7 +89,7 @@ default void delete(@Nullable User user, @Nullable String auditUserComment) thro } void save(User user) throws ChangePropertyDescriptorException; void save(User user, boolean allowAddBaseProperty) throws ChangePropertyDescriptorException; - void save(User user, @Nullable String auditComment, @Nullable String auditUserComment) throws ChangePropertyDescriptorException; + void save(User user, @Nullable String auditComment, @Nullable String auditUserComment, @Nullable Map oldRecordMap, @Nullable Map newRecordMap) throws ChangePropertyDescriptorException; /** Returns true if this domain has not yet been saved. */ boolean isNew(); diff --git a/api/src/org/labkey/api/exp/property/DomainAuditProvider.java b/api/src/org/labkey/api/exp/property/DomainAuditProvider.java index efa4423e0fc..ca4a00ae4e8 100644 --- a/api/src/org/labkey/api/exp/property/DomainAuditProvider.java +++ b/api/src/org/labkey/api/exp/property/DomainAuditProvider.java @@ -19,6 +19,7 @@ import org.labkey.api.audit.AbstractAuditTypeProvider; import org.labkey.api.audit.AuditTypeEvent; import org.labkey.api.audit.AuditTypeProvider; +import org.labkey.api.audit.DetailedAuditTypeEvent; import org.labkey.api.audit.query.AbstractAuditDomainKind; import org.labkey.api.audit.query.DefaultAuditTypeTable; import org.labkey.api.data.ColumnInfo; @@ -31,6 +32,7 @@ import org.labkey.api.data.TableInfo; import org.labkey.api.exp.PropertyDescriptor; import org.labkey.api.exp.PropertyType; +import org.labkey.api.query.DetailsURL; import org.labkey.api.query.FieldKey; import org.labkey.api.query.UserSchema; import org.labkey.api.util.PageFlowUtil; @@ -98,7 +100,7 @@ public String getDescription() @Override public TableInfo createTableInfo(UserSchema userSchema, ContainerFilter cf) { - return new DefaultAuditTypeTable(this, createStorageTableInfo(), userSchema, cf, defaultVisibleColumns) + DefaultAuditTypeTable table = new DefaultAuditTypeTable(this, createStorageTableInfo(), userSchema, cf, defaultVisibleColumns) { @Override protected void initColumn(MutableColumnInfo col) @@ -112,6 +114,14 @@ else if (COLUMN_NAME_USER_COMMENT.equalsIgnoreCase(col.getName())) col.setLabel("User Comment"); } }; + + appendValueMapColumns(table); + + DetailsURL url = DetailsURL.fromString("audit-detailedAuditChanges.view?auditRowId=${rowId}&auditEventType=" + EVENT_TYPE); + url.setStrictContainerContextEval(true); + table.setDetailsURL(url); + + return table; } @Override @@ -135,7 +145,7 @@ public Class getEventClass() return (Class)DomainAuditEvent.class; } - public static class DomainAuditEvent extends AuditTypeEvent + public static class DomainAuditEvent extends DetailedAuditTypeEvent { private String _domainUri; private String _domainName; @@ -195,6 +205,8 @@ public DomainAuditDomainKind() Set fields = new LinkedHashSet<>(); fields.add(createPropertyDescriptor(COLUMN_NAME_DOMAIN_URI, PropertyType.STRING)); fields.add(createPropertyDescriptor(COLUMN_NAME_DOMAIN_NAME, PropertyType.STRING)); + fields.add(createOldDataMapPropertyDescriptor()); + fields.add(createNewDataMapPropertyDescriptor()); fields.add(createPropertyDescriptor(COLUMN_NAME_USER_COMMENT, PropertyType.STRING)); _fields = Collections.unmodifiableSet(fields); } diff --git a/api/src/org/labkey/api/exp/property/DomainProperty.java b/api/src/org/labkey/api/exp/property/DomainProperty.java index 2e0211eb201..3a44dd104bb 100644 --- a/api/src/org/labkey/api/exp/property/DomainProperty.java +++ b/api/src/org/labkey/api/exp/property/DomainProperty.java @@ -157,6 +157,5 @@ default boolean isUniqueIdField() boolean isScannable(); void setScannable(boolean scannable); - String getPropertyValidatorStringVal(); void setOldPropertyDescriptor(PropertyDescriptor oldPropertyDescriptor); } diff --git a/api/src/org/labkey/api/exp/property/DomainPropertyAuditProvider.java b/api/src/org/labkey/api/exp/property/DomainPropertyAuditProvider.java index 2778047abf9..b8b732e53bc 100644 --- a/api/src/org/labkey/api/exp/property/DomainPropertyAuditProvider.java +++ b/api/src/org/labkey/api/exp/property/DomainPropertyAuditProvider.java @@ -17,6 +17,7 @@ import org.labkey.api.audit.AbstractAuditTypeProvider; import org.labkey.api.audit.AuditTypeEvent; +import org.labkey.api.audit.DetailedAuditTypeEvent; import org.labkey.api.audit.query.AbstractAuditDomainKind; import org.labkey.api.audit.query.DefaultAuditTypeTable; import org.labkey.api.data.Container; @@ -105,7 +106,7 @@ public List getDefaultVisibleColumns() @Override public TableInfo createTableInfo(UserSchema userSchema, ContainerFilter cf) { - return new DefaultAuditTypeTable(this, createStorageTableInfo(), userSchema, cf, DEFAULT_VISIBLE_COLUMNS) + DefaultAuditTypeTable table = new DefaultAuditTypeTable(this, createStorageTableInfo(), userSchema, cf, DEFAULT_VISIBLE_COLUMNS) { @Override protected void initColumn(MutableColumnInfo col) @@ -137,6 +138,14 @@ public TableInfo getLookupTableInfo() col.setLabel("Domain Name"); } }; + + appendValueMapColumns(table); + + DetailsURL url = DetailsURL.fromString("audit-detailedAuditChanges.view?auditRowId=${rowId}&auditEventType=" + EVENT_NAME); + url.setStrictContainerContextEval(true); + table.setDetailsURL(url); + + return table; } public static class DomainPropertyAuditDomainKind extends AbstractAuditDomainKind @@ -156,6 +165,8 @@ public DomainPropertyAuditDomainKind() fields.add(createPropertyDescriptor(COLUMN_NAME_ACTION, PropertyType.STRING)); fields.add(createPropertyDescriptor(COLUMN_NAME_DOMAIN_NAME, PropertyType.STRING)); fields.add(createPropertyDescriptor(COLUMN_NAME_DOMAIN_EVENT_ID, PropertyType.BIGINT)); + fields.add(createOldDataMapPropertyDescriptor()); + fields.add(createNewDataMapPropertyDescriptor()); _fields = Collections.unmodifiableSet(fields); } @@ -178,7 +189,7 @@ public String getKindName() } } - public static class DomainPropertyAuditEvent extends AuditTypeEvent + public static class DomainPropertyAuditEvent extends DetailedAuditTypeEvent { private String _propertyUri; private String _propertyName; diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index ef80b3be488..69e4ea74484 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -93,6 +93,7 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -747,7 +748,7 @@ public static ValidationException updateDomainDescriptor(GWTDomain orig, GWTDomain update, Container container, User user, boolean updateDomainName, @Nullable String auditComment) { - return updateDomainDescriptor(orig, update, container, user, updateDomainName, auditComment, null); + return updateDomainDescriptor(orig, update, container, user, updateDomainName, auditComment, null, null, null); } public static String getStringPropChangeMsg(String propertyName, String oldProp, String newProp) @@ -805,7 +806,8 @@ public static String getCollectionPropChangeMsg(String propertyName, Collect /** @return Errors encountered during the save attempt */ @NotNull - public static ValidationException updateDomainDescriptor(GWTDomain orig, GWTDomain update, Container container, User user, boolean updateDomainName, @Nullable String auditComment, @Nullable String auditUserComment) + public static ValidationException updateDomainDescriptor(GWTDomain orig, GWTDomain update, Container container, User user, + boolean updateDomainName, @Nullable String auditComment, @Nullable String auditUserComment, @Nullable Map oldProps, @Nullable Map newProps) { LOG.info("Updating domain descriptor for " + orig.getName()); assert orig.getDomainURI().equals(update.getDomainURI()); @@ -852,7 +854,20 @@ public static ValidationException updateDomainDescriptor(GWTDomain oldIndices = GWTIndex.toStringVals(orig.getIndices()); + if (oldIndices != null) + { + if (oldProps == null) + oldProps = new LinkedHashMap<>(); + oldProps.put("Indices", oldIndices); + } + List newIndices = GWTIndex.toStringVals(update.getIndices()); + if (newIndices != null) + { + if (newProps == null) + newProps = new LinkedHashMap<>(); + newProps.put("Indices", newIndices); + } // NOTE that DomainImpl.save() does an optimistic concurrency check, but we still need to check here. // This code is diff'ing two GWTDomains and applying those changes to Domain d. We need to make sure we're @@ -975,7 +990,7 @@ public static ValidationException updateDomainDescriptor(GWTDomain(defaultValues); try @@ -1262,18 +1277,15 @@ private static List> updatePropertyValidators(DomainProperty for (GWTPropertyValidator v : newPd.getPropertyValidators()) { if (v.getRowId() != 0) - { - hasChange = true; newProps.put(v.getRowId(), v); - } else { Lsid lsid = DefaultPropertyValidator.createValidatorURI(v.getType()); IPropertyValidator pv = PropertyService.get().createValidator(lsid.toString()); - boolean change = _copyValidator(pv, v); - hasChange = hasChange || change; + _copyValidator(pv, v); dp.addValidator(pv); + hasChange = true; } if (v.getExtraProperties() != null && v.getExtraProperties().containsKey("valueUpdates")) diff --git a/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java b/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java index d9006a21c23..14d7c2c1cf8 100644 --- a/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java @@ -61,6 +61,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -564,4 +565,27 @@ public void setImportAliasMapJson(String aliasJson) _object.setDataParentImportAliasMap(aliasJson); } + public Map getAuditRecordMap() + { + Map map = new LinkedHashMap<>(); + if (!StringUtils.isEmpty(getNameExpression())) + map.put("NameExpression", getNameExpression()); + String importAliasStr = null; + try + { + importAliasStr = ExperimentJSONConverter.getImportAliasStringVal(getImportAliasMap()); + } + catch (IOException ignore) + { + } + if (!StringUtils.isEmpty(importAliasStr)) + map.put("ImportAlias", importAliasStr); + if (!StringUtils.isEmpty(getCategory())) + map.put("Category", getCategory()); + if (getSampleType() != null) + map.put("SampleType", getSampleType().getRowId()); + + return map; + } + } diff --git a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java index 515ebde411a..5d053defa4f 100644 --- a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java @@ -80,6 +80,7 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -1112,4 +1113,37 @@ public boolean isMedia() { return ExpSchema.SampleTypeCategoryType.media.name().equalsIgnoreCase(getCategory()); } + + public Map getAuditRecordMap() + { + Map map = new LinkedHashMap<>(); + if (!StringUtils.isEmpty(getName())) + map.put("Name", getName()); + if (!StringUtils.isEmpty(getNameExpression())) + map.put("nameExpression", getNameExpression()); + if (!StringUtils.isEmpty(getAliquotNameExpression())) + map.put("AliquotNameExpression", getAliquotNameExpression()); + if (!StringUtils.isEmpty(getLabelColor())) + map.put("LabelColor", getLabelColor()); + if (!StringUtils.isEmpty(getMetricUnit())) + map.put("MetricUnit", getMetricUnit()); + String importAliasStr = null; + try + { + importAliasStr = ExperimentJSONConverter.getImportAliasStringVal(getImportAliasMap()); + } + catch (IOException ignore) + { + } + if (!StringUtils.isEmpty(importAliasStr)) + map.put("ImportAlias", importAliasStr); + if (getAutoLinkTargetContainer() != null) + map.put("AutoLinkTargetContainerId", getAutoLinkTargetContainer().getId()); + if (!StringUtils.isEmpty(getAutoLinkCategory())) + map.put("AutoLinkCategory", getAutoLinkCategory()); + if (!StringUtils.isEmpty(getCategory())) + map.put("Category", getCategory()); + + return map; + } } diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index f062fa6d0d5..3c909f21082 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -7966,12 +7966,17 @@ else if (domain.getPropertyByName(propertyName) != null) // issue 25275 } @Override - public ValidationException updateDataClass(@NotNull Container c, @NotNull User u, @NotNull ExpDataClass dataClass, + public ValidationException updateDataClass(@NotNull Container c, @NotNull User u, @NotNull ExpDataClass dc, @Nullable DataClassDomainKindProperties properties, GWTDomain original, GWTDomain update, @Nullable String auditUserComment) { + ExpDataClassImpl dataClass = (ExpDataClassImpl) dc; + + Map oldProps = dataClass.getAuditRecordMap(); + Map newProps = properties != null ? properties.getAuditRecordMap() : dataClass.getAuditRecordMap() /* no update */; + ValidationException errors; StringBuilder changeDetails = new StringBuilder(); @@ -7980,10 +7985,17 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u boolean hasNameChange = false; String oldDataClassName = dataClass.getName(); String newName = null; + + oldProps.put("Name", oldDataClassName); + newProps.put("Name", oldDataClassName); // to be updated by options + oldProps.put("Description", dataClass.getDescription()); + newProps.put("Description", dataClass.getDescription()); // to be updated by options + if (options != null) { validateDataClassOptions(c, u, options); newName = StringUtils.trimToNull(options.getName()); + newProps.put("Name", newName); if (!oldDataClassName.equals(newName)) { validateDataClassName(c, u, newName, oldDataClassName.equalsIgnoreCase(newName)); @@ -7991,13 +8003,10 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u dataClass.setName(newName); changeDetails.append("The name of the data class '" + oldDataClassName + "' was changed to '" + newName + "'."); } - changeDetails.append(DomainUtil.getStringPropChangeMsg("Description", dataClass.getDescription(), options.getDescription())); + newProps.put("Description", options.getDescription()); dataClass.setDescription(options.getDescription()); - changeDetails.append(DomainUtil.getStringPropChangeMsg("NameExpression", dataClass.getNameExpression(), options.getNameExpression())); dataClass.setNameExpression(options.getNameExpression()); - changeDetails.append(DomainUtil.getPropChangeMsg("SampleType", dataClass.getSampleType() == null ? null : dataClass.getSampleType().getRowId(), options.getSampleType() == null ? null : options.getSampleType())); dataClass.setSampleType(options.getSampleType()); - changeDetails.append(DomainUtil.getStringPropChangeMsg("Category", dataClass.getCategory(), options.getCategory())); dataClass.setCategory(options.getCategory()); Map> newAliases = options.getImportAliases(); if (newAliases != null && !newAliases.isEmpty()) @@ -8014,18 +8023,7 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u throw new RuntimeException(e); } } - String oldImportAliasStr = null; - try - { - oldImportAliasStr = ExperimentJSONConverter.getImportAliasStringVal(dataClass.getImportAliasMap()); - } - catch (IOException e) - { - throw new RuntimeException(e); - } dataClass.setImportAliasMap(newAliases); - String newImportAliasStr = ExperimentJSONConverter.getImportAliasStringVal(newAliases); - changeDetails.append(DomainUtil.getStringPropChangeMsg("ImportAlias", oldImportAliasStr, newImportAliasStr)); if (!NameExpressionOptionService.get().allowUserSpecifiedNames(c) && options.getNameExpression() == null) throw new ApiUsageException(c.hasProductFolders() ? NAME_EXPRESSION_REQUIRED_MSG_WITH_SUBFOLDERS : NAME_EXPRESSION_REQUIRED_MSG); @@ -8041,9 +8039,13 @@ public ValidationException updateDataClass(@NotNull Container c, @NotNull User u QueryChangeListener.QueryPropertyChange.handleQueryNameChange(oldDataClassName, newName, schemaKey, u, c); if (options != null && options.getExcludedContainerIds() != null) - changeDetails.append(ExperimentService.get().ensureDataTypeContainerExclusions(DataTypeForExclusion.DataClass, options.getExcludedContainerIds(), dataClass.getRowId(), u)); + { + Pair, Collection> exclusionChanges = ExperimentService.get().ensureDataTypeContainerExclusions(DataTypeForExclusion.DataClass, options.getExcludedContainerIds(), dataClass.getRowId(), u); + oldProps.put("ContainerExclusions", exclusionChanges.first); + newProps.put("ContainerExclusions", exclusionChanges.second); + } - errors = DomainUtil.updateDomainDescriptor(original, update, c, u, hasNameChange, changeDetails.toString(), auditUserComment); + errors = DomainUtil.updateDomainDescriptor(original, update, c, u, hasNameChange, changeDetails.toString(), auditUserComment, oldProps, newProps); QueryService.get().saveCalculatedFieldsMetadata(schemaKey.toString(), update.getQueryName(), hasNameChange ? newName : null, update.getCalculatedFields(), !original.getCalculatedFields().isEmpty(), u, c); @@ -8875,12 +8877,12 @@ public void ensureContainerDataTypeExclusions(@NotNull DataTypeForExclusion data @Override @NotNull - public String ensureDataTypeContainerExclusions(@NotNull DataTypeForExclusion dataType, @Nullable Collection excludedContainerIds, @NotNull Integer dataTypeId, User user) + public Pair, Collection> ensureDataTypeContainerExclusions(@NotNull DataTypeForExclusion dataType, @Nullable Collection excludedContainerIds, @NotNull Integer dataTypeId, User user) { Set previousExclusions = getDataTypeContainerExclusions(dataType, dataTypeId); if (excludedContainerIds == null) - return DomainUtil.getCollectionPropChangeMsg("ContainerExclusions", previousExclusions, null); + return new Pair<>(previousExclusions, null); Set updatedExclusions = new HashSet<>(excludedContainerIds); @@ -8908,10 +8910,7 @@ public String ensureDataTypeContainerExclusions(@NotNull DataTypeForExclusion da } } - if (!toAdd.isEmpty() || !toRemove.isEmpty()) - return DomainUtil.getCollectionPropChangeMsg("ContainerExclusions", previousExclusions, updatedExclusions); - - return ""; + return new Pair<>(previousExclusions, updatedExclusions); } @Override diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index 0e87d71edf8..62b20dbc731 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -1007,8 +1007,14 @@ public ValidationException updateSampleType(GWTDomain oldProps = st.getAuditRecordMap(); + Map newProps = options != null ? options.getAuditRecordMap() : st.getAuditRecordMap() /* no update */; + String newName = StringUtils.trimToNull(update.getName()); String oldSampleTypeName = st.getName(); + oldProps.put("Name", oldSampleTypeName); + newProps.put("Name", newName); + boolean hasNameChange = false; if (!oldSampleTypeName.equals(newName)) { @@ -1020,11 +1026,10 @@ public ValidationException updateSampleType(GWTDomain, Collection> exclusionChanges = ExperimentService.get().ensureDataTypeContainerExclusions(ExperimentService.DataTypeForExclusion.SampleType, options.getExcludedContainerIds(), st.getRowId(), user); + oldProps.put("ContainerExclusions", exclusionChanges.first); + newProps.put("ContainerExclusions", exclusionChanges.second); + } if (options != null && options.getExcludedDashboardContainerIds() != null) { - String change = ExperimentService.get().ensureDataTypeContainerExclusions(ExperimentService.DataTypeForExclusion.DashboardSampleType, options.getExcludedDashboardContainerIds(), st.getRowId(), user); - if (!StringUtils.isEmpty(change)) - changeDetails.append("Dashboard ").append(change); + Pair, Collection> exclusionChanges = ExperimentService.get().ensureDataTypeContainerExclusions(ExperimentService.DataTypeForExclusion.DashboardSampleType, options.getExcludedDashboardContainerIds(), st.getRowId(), user); + oldProps.put("DashboardContainerExclusions", exclusionChanges.first); + newProps.put("DashboardContainerExclusions", exclusionChanges.second); } - errors = DomainUtil.updateDomainDescriptor(original, update, container, user, hasNameChange, changeDetails.toString(), auditUserComment); + errors = DomainUtil.updateDomainDescriptor(original, update, container, user, hasNameChange, changeDetails.toString(), auditUserComment, oldProps, newProps); if (!errors.hasErrors()) { diff --git a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java index 93b67c7fcf8..7c945a00a3a 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable; import org.json.JSONArray; import org.json.JSONObject; +import org.labkey.api.audit.AbstractAuditTypeProvider; import org.labkey.api.audit.AuditLogService; import org.labkey.api.audit.AuditTypeEvent; import org.labkey.api.collections.CaseInsensitiveHashMap; @@ -57,10 +58,6 @@ import org.labkey.api.exp.property.DomainPropertyAuditProvider; import org.labkey.api.exp.property.DomainTemplate; import org.labkey.api.exp.property.DomainUtil; -import org.labkey.api.exp.property.IPropertyValidator; -import org.labkey.api.exp.property.Lookup; -import org.labkey.api.exp.property.PropertyService; -import org.labkey.api.gwt.client.DefaultValueType; import org.labkey.api.gwt.client.model.GWTIndex; import org.labkey.api.query.BatchValidationException; import org.labkey.api.query.FieldKey; @@ -90,6 +87,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.concurrent.locks.Lock; @@ -366,7 +364,7 @@ public void delete(@Nullable User user, @Nullable String auditUserComment) throw DefaultValueService.get().clearDefaultValues(getContainer(), this); OntologyManager.deleteDomain(getTypeURI(), getContainer()); StorageProvisioner.get().drop(this); - addAuditEvent(user, String.format("The domain %s was deleted", _dd.getName()), auditUserComment); + addAuditEvent(user, String.format("The domain %s was deleted", _dd.getName()), auditUserComment, getContainer(), null, null); DomainPropertyManager.clearCaches(); transaction.commit(); } @@ -527,22 +525,22 @@ private void validatePropertyLookup(User user, DomainProperty dp) throws ChangeP public void saveIfNotExists(User user) throws ChangePropertyDescriptorException { - save(user, false, true, null, null); + save(user, false, true, null, null, null, null); } @Override public void save(User user, boolean allowAddBaseProperty) throws ChangePropertyDescriptorException { - save(user, false, false, null, null); + save(user, false, false, null, null, null, null); } @Override - public void save(User user, @Nullable String auditComment, @Nullable String auditUserComment) throws ChangePropertyDescriptorException + public void save(User user, @Nullable String auditComment, @Nullable String auditUserComment, @Nullable Map oldRecordMap, @Nullable Map newRecordMap) throws ChangePropertyDescriptorException { - save(user, false, false, auditComment, auditUserComment); + save(user, false, false, auditComment, auditUserComment, oldRecordMap, newRecordMap); } - public void save(User user, boolean allowAddBaseProperty, boolean saveOnlyIfNotExists, @Nullable String auditComment, @Nullable String auditUserComment) throws ChangePropertyDescriptorException + public void save(User user, boolean allowAddBaseProperty, boolean saveOnlyIfNotExists, @Nullable String auditComment, @Nullable String auditUserComment, @Nullable Map oldRecordMap, @Nullable Map newRecordMap) throws ChangePropertyDescriptorException { ExperimentService exp = ExperimentService.get(); @@ -737,7 +735,7 @@ else if (impl.isNew()) boolean isImplNew = impl.isNew(); PropertyDescriptor pdOld = impl._pdOld; String oldValidators = null != pdOld ? PropertyChangeAuditInfo.renderValidators(pdOld) : null; - String oldFormats = null != pdOld ? PropertyChangeAuditInfo.renderConditionalFormats(pdOld) : null; + String oldConditionalFormats = null != pdOld ? PropertyChangeAuditInfo.renderConditionalFormats(pdOld) : null; impl.save(user, _dd, sortOrder++); // Automatically preserve order String defaultValue = impl.getDefaultValue(); @@ -748,7 +746,7 @@ else if (impl.isNew()) propertyAuditInfo.add(new PropertyChangeAuditInfo(impl, true)); else if (null != pdOld) { - PropertyChangeAuditInfo auditInfo = new PropertyChangeAuditInfo(impl, newPropName, pdOld, oldValidators, oldFormats); + PropertyChangeAuditInfo auditInfo = new PropertyChangeAuditInfo(impl, newPropName, pdOld, oldValidators, oldConditionalFormats); if (auditInfo.isChanged()) propertyAuditInfo.add(auditInfo); } @@ -823,16 +821,16 @@ else if (null != pdOld) Runnable afterDomainCommit = () -> { if (isDomainNew) - addAuditEvent(user, extraAuditComment + String.format("The domain %s was created", _dd.getName()), auditUserComment); + addAuditEvent(user, extraAuditComment + String.format("The domain %s was created", _dd.getName()), auditUserComment, getContainer(), oldRecordMap, newRecordMap); if (finalPropChanged) { - final Long domainEventId = addAuditEvent(user, extraAuditComment + String.format("The column(s) of domain %s were modified", _dd.getName()), auditUserComment); - propertyAuditInfo.forEach(auditInfo -> addPropertyAuditEvent(user, auditInfo.getProp(), auditInfo.getAction(), domainEventId, getName(), auditInfo.getDetails())); + final Long domainEventId = addAuditEvent(user, extraAuditComment + String.format("The column(s) of domain %s were modified", _dd.getName()), auditUserComment, getContainer(), oldRecordMap, newRecordMap); + propertyAuditInfo.forEach(auditInfo -> addPropertyAuditEvent(user, getContainer(), auditInfo.getProp(), auditInfo.getAction(), domainEventId, getName(), auditInfo.getDetails())); } else if (!isDomainNew) { - addAuditEvent(user, extraAuditComment + String.format("The descriptor of domain %s was updated", _dd.getName()), auditUserComment); + addAuditEvent(user, extraAuditComment + String.format("The descriptor of domain %s was updated", _dd.getName()), auditUserComment, getContainer(), oldRecordMap, newRecordMap); } }; transaction.addCommitTask(afterDomainCommit, DbScope.CommitTaskOption.POSTCOMMIT); @@ -943,11 +941,14 @@ private void checkAndThrowSizeConstraints(DomainKind kind, DomainProperty pro } } - private Long addAuditEvent(@Nullable User user, String comment, @Nullable String auditUserComment) + private Long addAuditEvent(@Nullable User user, String comment, @Nullable String auditUserComment, @Nullable Container container, + @Nullable Map oldProps, @Nullable Map newProps) { if (user != null) { DomainAuditProvider.DomainAuditEvent event = new DomainAuditProvider.DomainAuditEvent(getContainer(), comment); + event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, oldProps)); + event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, newProps)); event.setUserComment(auditUserComment); event.setDomainUri(getTypeURI()); @@ -959,42 +960,42 @@ private Long addAuditEvent(@Nullable User user, String comment, @Nullable String return null; } - private void addPropertyAuditEvent(@Nullable User user, DomainProperty prop, String action, Long domainEventId, String domainName, String comment) + private void addPropertyAuditEvent(@Nullable User user, Container container, DomainProperty prop, String action, Long domainEventId, String domainName, PropertyChangeAuditInfoDetail changeDetail) { + String changeSummary = changeDetail == null ? null : changeDetail.changeSummary; + Map oldProps = changeDetail == null ? null : changeDetail.oldRecordMap; + Map newProps = changeDetail == null ? null : changeDetail.newRecordMap; DomainPropertyAuditProvider.DomainPropertyAuditEvent event = new DomainPropertyAuditProvider.DomainPropertyAuditEvent(getContainer(), prop.getPropertyURI(), prop.getName(), - action, domainEventId, domainName, comment); + action, domainEventId, domainName, changeSummary); + event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, oldProps)); + event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, newProps)); AuditLogService.get().addEvent(user, event); } - public static String getPropertyValidatorStringVal(Collection validators) + record PropertyChangeAuditInfoDetail(String changeSummary, Map oldRecordMap, Map newRecordMap) { - if (validators == null || validators.isEmpty()) - return ""; - List strings = new ArrayList<>(); - validators.forEach(validator -> strings.add(validator.getStringVal())); - return StringUtils.join(strings, ", "); } private static class PropertyChangeAuditInfo { private final DomainProperty _prop; private final String _action; - private final String _details; // to go in comments + private final PropertyChangeAuditInfoDetail _details; // to go in comments public PropertyChangeAuditInfo(DomainPropertyImpl prop, boolean isCreated) { _prop = prop; _action = isCreated ? "Created" : "Deleted"; - _details = isCreated ? makeNewPropAuditComment(prop) : ""; + _details = isCreated ? makeNewPropAuditComment(prop) : null; } public PropertyChangeAuditInfo(DomainPropertyImpl prop, String newPropName, PropertyDescriptor pdOld, - String oldValidators, String oldFormats) + String oldValidators, String oldConditionalFormats) { _prop = prop; _action = "Modified"; - _details = makeModifiedPropAuditComment(prop, newPropName, pdOld, oldValidators, oldFormats); + _details = makeModifiedPropAuditComment(prop, newPropName, pdOld, oldValidators, oldConditionalFormats); } public DomainProperty getProp() @@ -1007,123 +1008,49 @@ public String getAction() return _action; } - public String getDetails() + public PropertyChangeAuditInfoDetail getDetails() { return _details; } - public boolean isChanged() { return !_details.isEmpty(); } + public boolean isChanged() + { + return !StringUtils.isEmpty(_details.changeSummary); + } - private String makeNewPropAuditComment(DomainProperty prop) + private PropertyChangeAuditInfoDetail makeNewPropAuditComment(DomainPropertyImpl prop) { - StringBuilder str = new StringBuilder(); - str.append("Name: ").append(prop.getName()).append("; "); - str.append("Label: ").append(renderCheckingBlank(prop.getLabel())).append("; "); - str.append("Type: ").append(prop.getPropertyType().getXarName()).append("; "); - if (prop.getPropertyType().getJdbcType().isText()) - str.append("Scale: ").append(prop.getScale()).append("; "); - - Lookup lookup = prop.getLookup(); - if (null != lookup) - { - str.append("Lookup: ["); - if (null != lookup.getContainer()) - str.append("Container: ").append(lookup.getContainer().getName()).append(", "); - str.append("Schema: ").append(lookup.getSchemaKey()).append(", ") - .append("Query: ").append(lookup.getQueryName()).append("]; "); - } + String newValidators = PropertyChangeAuditInfo.renderValidators(prop.getPropertyDescriptor()); + String newConditionalFormats = PropertyChangeAuditInfo.renderConditionalFormats(prop.getPropertyDescriptor()); + Map newProps = prop.getAuditRecordMap(newValidators, newConditionalFormats); - str.append("Description: ").append(renderCheckingBlank(prop.getDescription())).append("; "); - str.append("Format: ").append(renderCheckingBlank(prop.getFormat())).append("; "); - str.append("URL: ").append(renderCheckingBlank(prop.getURL())).append("; "); - str.append("PHI: ").append(prop.getPHI().toString()).append("; "); - str.append("ImportAliases: ").append(renderImportAliases(prop.getPropertyDescriptor())).append("; "); - str.append("Validators: ").append(renderValidators(prop.getPropertyDescriptor())).append("; "); - str.append("ConditionalFormats: ").append(renderConditionalFormats(prop.getPropertyDescriptor())).append("; "); - str.append("DefaultValueType: ").append(renderDefaultValueType(prop.getPropertyDescriptor())).append("; "); - str.append("DefaultScale: ").append(prop.getDefaultScale().getLabel()).append("; "); - str.append("Required: ").append(renderBool(prop.isRequired())).append("; "); - str.append("Hidden: ").append(renderBool(prop.isHidden())).append("; "); - str.append("MvEnabled: ").append(renderBool(prop.isMvEnabled())).append("; "); - str.append("Measure: ").append(renderBool(prop.isMeasure())).append("; "); - str.append("Dimension: ").append(renderBool(prop.isDimension())).append("; "); - str.append("ShownInInsert: ").append(renderBool(prop.isShownInInsertView())).append("; "); - str.append("ShownInDetails: ").append(renderBool(prop.isShownInDetailsView())).append("; "); - str.append("ShownInUpdate: ").append(renderBool(prop.isShownInUpdateView())).append("; "); - str.append("RecommendedVariable: ").append(renderBool(prop.isRecommendedVariable())).append("; "); - str.append("ExcludedFromShifting: ").append(renderBool(prop.isExcludeFromShifting())).append("; "); - str.append("Scannable: ").append(renderBool(prop.isScannable())).append("; "); - return str.toString(); + return new PropertyChangeAuditInfoDetail(null, null, newProps); } - private String makeModifiedPropAuditComment(DomainPropertyImpl prop, String newPropName, PropertyDescriptor pdOld, String oldValidators, String oldFormats) + private PropertyChangeAuditInfoDetail makeModifiedPropAuditComment(DomainPropertyImpl prop, String newPropName, PropertyDescriptor pdOld, String oldValidators, String oldConditionalFormats) { - StringBuilder str = new StringBuilder(); - if (!pdOld.getName().equals(newPropName)) - str.append("Name: ").append(renderOldVsNew(pdOld.getName(), newPropName)).append("; "); - if (!StringUtils.equals(pdOld.getLabel(), prop.getLabel())) - str.append("Label: ").append(renderOldVsNew(renderCheckingBlank(pdOld.getLabel()), renderCheckingBlank(prop.getLabel()))).append("; "); - if (null != pdOld.getPropertyType() && !pdOld.getPropertyType().equals(prop.getPropertyType())) - str.append("Type: ").append(renderOldVsNew(pdOld.getPropertyType().getXarName(), prop.getPropertyType().getXarName())).append("; "); - if (prop.getPropertyType().getJdbcType().isText()) - if (pdOld.getScale() != prop.getScale()) - str.append("Scale: ").append(renderOldVsNew(Integer.toString(pdOld.getScale()), Integer.toString(prop.getScale()))).append("; "); - - if (!StringUtils.equals(pdOld.getLookupSchema(), prop.getPropertyDescriptor().getLookupSchema()) || - !StringUtils.equals(pdOld.getLookupQuery(), prop.getPropertyDescriptor().getLookupQuery()) || - !StringUtils.equals(pdOld.getLookupContainer(), prop.getPropertyDescriptor().getLookupContainer())) + Map oldProps = pdOld.getAuditRecordMap(oldValidators, oldConditionalFormats); + + String newValidators = PropertyChangeAuditInfo.renderValidators(prop.getPropertyDescriptor()); + String newConditionalFormats = PropertyChangeAuditInfo.renderConditionalFormats(prop.getPropertyDescriptor()); + Map newProps = prop.getAuditRecordMap(newValidators, newConditionalFormats); + newProps.put("Name", newPropName); + + List changed = new ArrayList<>(); + for (String oldKey : oldProps.keySet()) { - renderLookupDiff(prop.getPropertyDescriptor(), pdOld, str); + if (!Objects.equals(oldProps.get(oldKey), newProps.get(oldKey))) + changed.add(oldKey); + } + for (String newKey : newProps.keySet()) + { + if (!Objects.equals(oldProps.get(newKey), newProps.get(newKey)) && !changed.contains(newKey)) + changed.add(newKey); } - if (!StringUtils.equals(pdOld.getDescription(), prop.getDescription())) - str.append("Description: ").append(renderOldVsNew(renderCheckingBlank(pdOld.getDescription()), renderCheckingBlank(prop.getDescription()))).append("; "); - if (!StringUtils.equals(pdOld.getFormat(), prop.getFormat())) - str.append("Format: ").append(renderOldVsNew(renderCheckingBlank(pdOld.getFormat()), renderCheckingBlank(prop.getFormat()))).append("; "); - if (!StringUtils.equals((null != pdOld.getURL() ? pdOld.getURL().toString() : null), prop.getURL())) - str.append("URL: ").append(renderOldVsNew(renderCheckingBlank(null != pdOld.getURL() ? pdOld.getURL().toString() : null), renderCheckingBlank(prop.getURL()))).append("; "); - if (!pdOld.getPHI().equals(prop.getPHI())) - str.append("PHI: ").append(renderOldVsNew(pdOld.getPHI().getLabel(), prop.getPHI().getLabel())).append("; "); - - renderImportAliasesDiff(prop, pdOld, str); - renderValidatorsDiff(prop, oldValidators, str); - renderConditionalFormatsDiff(prop, oldFormats, str); - renderDefaultValueTypeDiff(prop, pdOld, str); - - if (!pdOld.getDefaultScale().getLabel().equals(prop.getDefaultScale().getLabel())) - str.append("DefaultScale: ").append(renderOldVsNew(pdOld.getDefaultScale().getLabel(), prop.getDefaultScale().getLabel())).append("; "); - if (pdOld.isRequired() != prop.isRequired()) - str.append("Required: ").append(renderOldVsNew(renderBool(pdOld.isRequired()), renderBool(prop.isRequired()))).append("; "); - if (pdOld.isHidden() != prop.isHidden()) - str.append("Hidden: ").append(renderOldVsNew(renderBool(pdOld.isHidden()), renderBool(prop.isHidden()))).append("; "); - if (pdOld.isMvEnabled() != prop.isMvEnabled()) - str.append("MvEnabled: ").append(renderOldVsNew(renderBool(pdOld.isMvEnabled()), renderBool(prop.isMvEnabled()))).append("; "); - if (pdOld.isMeasure() != prop.isMeasure()) - str.append("Measure: ").append(renderOldVsNew(renderBool(pdOld.isMeasure()), renderBool(prop.isMeasure()))).append("; "); - if (pdOld.isDimension() != prop.isDimension()) - str.append("Dimension: ").append(renderOldVsNew(renderBool(pdOld.isDimension()), renderBool(prop.isDimension()))).append("; "); - if (pdOld.isShownInInsertView() != prop.isShownInInsertView()) - str.append("ShownInInsert: ").append(renderOldVsNew(renderBool(pdOld.isShownInInsertView()), renderBool(prop.isShownInInsertView()))).append("; "); - if (pdOld.isShownInDetailsView() != prop.isShownInDetailsView()) - str.append("ShownInDetails: ").append(renderOldVsNew(renderBool(pdOld.isShownInDetailsView()), renderBool(prop.isShownInDetailsView()))).append("; "); - if (pdOld.isShownInUpdateView() != prop.isShownInUpdateView()) - str.append("ShownInUpdate: ").append(renderOldVsNew(renderBool(pdOld.isShownInUpdateView()), renderBool(prop.isShownInUpdateView()))).append("; "); - if (pdOld.isShownInLookupView() != prop.isShownInLookupView()) - str.append("ShownInLookupView: ").append(renderOldVsNew(renderBool(pdOld.isShownInLookupView()), renderBool(prop.isShownInLookupView()))).append("; "); - if (pdOld.isRecommendedVariable() != prop.isRecommendedVariable()) - str.append("RecommendedVariable: ").append(renderOldVsNew(renderBool(pdOld.isRecommendedVariable()), renderBool(prop.isRecommendedVariable()))).append("; "); - if (pdOld.isExcludeFromShifting() != prop.isExcludeFromShifting()) - str.append("ExcludedFromShifting: ").append(renderOldVsNew(renderBool(pdOld.isExcludeFromShifting()), renderBool(prop.isExcludeFromShifting()))).append("; "); - if (pdOld.isScannable() != prop.isScannable()) - str.append("Scannable: ").append(renderOldVsNew(renderBool(pdOld.isScannable()), renderBool(prop.isScannable()))).append("; "); - if (!StringUtils.equals(pdOld.getDerivationDataScope(), prop.getDerivationDataScope())) - str.append("DerivationDataScope: ").append(renderOldVsNew(renderCheckingBlank(pdOld.getDerivationDataScope()), renderCheckingBlank(prop.getDerivationDataScope()))).append("; "); - return str.toString(); - } - - private String renderCheckingBlank(String value) - { - return StringUtils.isNotBlank(value) ? value : ""; + String changeSummary = changed.isEmpty() ? null : + "The following " + (changed.size() > 1 ? "properties were " : "property was") + " updated: " + StringUtils.join(changed, ", "); + return new PropertyChangeAuditInfoDetail(changeSummary, oldProps, newProps); } private static String renderValidators(PropertyDescriptor prop) @@ -1138,86 +1065,15 @@ private static String renderConditionalFormats(PropertyDescriptor prop) return ConditionalFormat.toStringVal(formats); } - private void renderValidatorsDiff(DomainProperty prop, String oldValidators, StringBuilder str) - { - String validators = renderValidators(prop.getPropertyDescriptor()); - if (!StringUtils.equals(oldValidators, validators)) - str.append("Validators: ").append("old: ").append(oldValidators).append(", new: ").append(validators).append("; "); - } - - private void renderConditionalFormatsDiff(DomainProperty prop, String oldFormats, StringBuilder str) - { - String formats = renderConditionalFormats(prop.getPropertyDescriptor()); - if (!StringUtils.equals(oldFormats, formats)) - str.append("ConditionalFormats: ").append("old: ").append(oldFormats).append(", new: ").append(formats).append("; "); - } - - private String renderImportAliases(PropertyDescriptor prop) - { - Set aliases = prop.getImportAliasSet(); - if (aliases.isEmpty()) - return ""; - return StringUtils.join(aliases, ","); - } - - private void renderImportAliasesDiff(DomainProperty prop, PropertyDescriptor pdOld, StringBuilder str) - { - String oldAliases = renderImportAliases(pdOld); - String aliases = renderImportAliases(prop.getPropertyDescriptor()); - if (!StringUtils.equals(oldAliases, aliases)) - str.append("ImportAliases: ").append("old: ").append(oldAliases).append(", new: ").append(aliases).append("; "); - } - - private String renderDefaultValueType(PropertyDescriptor prop) - { - DefaultValueType type = prop.getDefaultValueTypeEnum(); - if (null == type) - return ""; - return type.getLabel(); - } - - private void renderDefaultValueTypeDiff(DomainProperty prop, PropertyDescriptor pdOld, StringBuilder str) - { - if (pdOld.getDefaultValueTypeEnum() != prop.getDefaultValueTypeEnum()) - str.append("DefaultValueType: ").append("old: ").append(renderDefaultValueType(pdOld)).append(", new: ") - .append(renderDefaultValueType(prop.getPropertyDescriptor())).append("; "); - } - - private String renderBool(boolean value) - { - return value ? "true" : "false"; - } - - private String renderOldVsNew(String oldVal, String newVal) - { - return oldVal + " -> " + newVal; - } - - private void renderLookupDiff(PropertyDescriptor pdNew, PropertyDescriptor pdOld, StringBuilder str) - { - str.append("Lookup: ["); - if (!StringUtils.equals(pdOld.getLookupContainer(), pdNew.getLookupContainer())) - str.append("Container: ").append("old: ").append(getContainerName(pdOld.getLookupContainer())).append(", new: ") - .append(getContainerName(pdNew.getLookupContainer())).append(", "); - if (!StringUtils.equals(pdOld.getLookupSchema(), pdNew.getLookupSchema())) - str.append("Schema: ").append("old: ").append(pdOld.getLookupSchema()).append(", new: ") - .append(pdNew.getLookupSchema()).append(", "); - if (!StringUtils.equals(pdOld.getLookupQuery(), pdNew.getLookupQuery())) - str.append("Query: ").append("old: ").append(pdOld.getLookupQuery()).append(", new: ") - .append(pdNew.getLookupQuery()); - str.append("]; "); - } + } - private String getContainerName(String containerId) - { - if (null != containerId) - { - Container container = ContainerManager.getForId(containerId); - if (null != container) - return container.getName(); - } + public static String getPropertyValidatorStringVal(Collection validators) + { + if (validators == null || validators.isEmpty()) return null; - } + List strings = new ArrayList<>(); + validators.forEach(validator -> strings.add(validator.getStringVal())); + return StringUtils.join(strings, ", "); } @Override diff --git a/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java index fb25d093c02..b8a9f16d2f0 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java @@ -55,9 +55,10 @@ import org.labkey.api.util.TestContext; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -643,12 +644,6 @@ public void setScannable(boolean scannable) edit().setScannable(scannable); } - @Override - public String getPropertyValidatorStringVal() - { - return DomainImpl.getPropertyValidatorStringVal(DomainPropertyManager.get().getValidators(this)); - } - @Override public void setOldPropertyDescriptor(PropertyDescriptor oldPropertyDescriptor) { @@ -1078,7 +1073,7 @@ public void setConditionalFormats(List formats) String newVal = ConditionalFormat.toStringVal(formats); String oldVal = ConditionalFormat.toStringVal(getConditionalFormats()); - if (!newVal.equals(oldVal)) + if (!Objects.equals(newVal, oldVal)) edit(); _formats = formats; @@ -1152,6 +1147,57 @@ public String toString() return super.toString() + _pd.getPropertyURI(); } + public Map getAuditRecordMap(@Nullable String validatorStr, @Nullable String conditionalFormatStr) + { + Map map = new LinkedHashMap<>(); + if (getName() != null) + map.put("Name", getName()); + if (getLabel() != null) + map.put("Label", getLabel()); + if (null != getPropertyType()) + map.put("Type", getPropertyType().getXarName()); + if (getPropertyType().getJdbcType().isText()) + map.put("Scale", getScale()); + if (getDescription() != null) + map.put("Description", getDescription()); + if (getFormat() != null) + map.put("Format", getFormat()); + if (getURL() != null) + map.put("URL", getURL()); + if (getPHI() != null) + map.put("PHI", getPHI().getLabel()); + if (getDefaultScale() != null) + map.put("DefaultScale", getDefaultScale().getLabel()); + map.put("Required", isRequired()); + map.put("Hidden", isHidden()); + map.put("MvEnabled", isMvEnabled()); + map.put("Measure", isMeasure()); + map.put("Dimension", isDimension()); + map.put("ShownInInsert", isShownInInsertView()); + map.put("ShownInDetails", isShownInDetailsView()); + map.put("ShownInUpdate", isShownInUpdateView()); + map.put("ShownInLookupView", isShownInLookupView()); + map.put("RecommendedVariable", isRecommendedVariable()); + map.put("ExcludedFromShifting", isExcludeFromShifting()); + map.put("Scannable", isScannable()); + if (getDerivationDataScope() != null) + map.put("DerivationDataScope", getDerivationDataScope()); + String importAliasStr = StringUtils.join(getImportAliasSet(), ","); + if (!StringUtils.isEmpty(importAliasStr)) + map.put("ImportAliases", importAliasStr); + if (getDefaultValueTypeEnum() != null) + map.put("DefaultValueType", getDefaultValueTypeEnum().getLabel()); + if (getLookup() != null) + map.put("Lookup", getLookup().toJSONString()); + + if (validatorStr != null) + map.put("Validator", validatorStr); + if (conditionalFormatStr != null) + map.put("ConditionalFormat", conditionalFormatStr); + + return map; + } + public static class TestCase extends Assert { private PropertyDescriptor _pd; diff --git a/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java b/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java index 8190626788c..8980e7e4700 100644 --- a/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java +++ b/experiment/src/org/labkey/experiment/api/property/PropertyValidator.java @@ -22,6 +22,9 @@ import org.labkey.api.util.UnexpectedException; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; public class PropertyValidator implements Serializable, Cloneable { @@ -162,7 +165,13 @@ public String getPropertyAuditStr() public String getStringVal() { - return getName() + ", " + getExpression() + ", " + getPropertyAuditStr() + ", " + getErrorMessage() + ", " + getDescription() + ", " + - StringUtils.replace(PropertyService.get().getValidatorKind(getTypeURI()).getName(), " Property Validator", ""); + List parts = new ArrayList<>(); + parts.add(getName()); + parts.add(getDescription()); + parts.add(getExpression()); + parts.add(getPropertyAuditStr()); + parts.add(getErrorMessage()); + parts.add(StringUtils.replace(PropertyService.get().getValidatorKind(getTypeURI()).getName(), " Property Validator", "")); + return parts.stream().filter(StringUtils::isNotBlank).collect(Collectors.joining(", ")); } } \ No newline at end of file diff --git a/experiment/src/org/labkey/experiment/api/property/PropertyValidatorImpl.java b/experiment/src/org/labkey/experiment/api/property/PropertyValidatorImpl.java index 86f6ded7c72..7de34645c51 100644 --- a/experiment/src/org/labkey/experiment/api/property/PropertyValidatorImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/PropertyValidatorImpl.java @@ -15,6 +15,7 @@ */ package org.labkey.experiment.api.property; +import org.apache.commons.lang3.StringUtils; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; import org.labkey.api.data.Table; @@ -65,6 +66,9 @@ public String getName() @Override public void setName(String name) { + if (StringUtils.equals(name, getName())) + return; + edit().setName(name); } @@ -77,6 +81,8 @@ public String getDescription() @Override public void setDescription(String description) { + if (StringUtils.equals(description, getDescription())) + return; edit().setDescription(description); } @@ -100,6 +106,9 @@ public String getExpressionValue() @Override public void setExpressionValue(String expression) { + if (StringUtils.equals(getExpressionValue(), expression)) + return; + edit().setExpression(expression); } @@ -140,6 +149,9 @@ public Map getProperties() @Override public void setErrorMessage(String message) { + if (StringUtils.equals(getErrorMessage(), message)) + return; + edit().setErrorMessage(message); } @@ -147,6 +159,9 @@ public void setErrorMessage(String message) public void setProperty(String key, String value) { Map props = getProperties(); + if (StringUtils.equals(props.get(key), value)) + return; + props.put(key, value); edit().setProperties(PageFlowUtil.toQueryString(props.entrySet())); } diff --git a/list/src/org/labkey/list/model/ListDomainKind.java b/list/src/org/labkey/list/model/ListDomainKind.java index 96c9c4f3da8..5999f465250 100644 --- a/list/src/org/labkey/list/model/ListDomainKind.java +++ b/list/src/org/labkey/list/model/ListDomainKind.java @@ -517,6 +517,8 @@ else if (ListService.get().getList(container, updatedName, false) != null) } //update list properties + Map oldProps = null; + Map newProps = null; if (null != listProperties) { if (listProperties.getDomainId() != original.getDomainId() || listProperties.getDomainId() != update.getDomainId()) @@ -524,14 +526,18 @@ else if (ListService.get().getList(container, updatedName, false) != null) if (!original.getDomainURI().equals(update.getDomainURI())) return exception.addGlobalError("domainURI mismatch between old and new domain"); - changeDetails.append(updateListProperties(container, user, listDefinition.getListId(), listProperties)); + Pair, Map> updatedProps = updateListProperties(container, user, listDefinition.getListId(), listProperties); + oldProps = updatedProps.first; + newProps = updatedProps.second; } // Issue 45042: Allow for the list description to be set via the save domain API calls else if (update.getDescription() != null) { listProperties = getListProperties(container, user, listDefinition.getListId()); listProperties.setDescription(update.getDescription()); - changeDetails.append(updateListProperties(container, user, listDefinition.getListId(), listProperties)); + Pair, Map> updatedProps = updateListProperties(container, user, listDefinition.getListId(), listProperties); + oldProps = updatedProps.first; + newProps = updatedProps.second; } //update domain design properties @@ -576,7 +582,7 @@ else if (update.getDescription() != null) } //update domain properties - exception.addErrors(DomainUtil.updateDomainDescriptor(original, update, container, user, hasNameChange, changeDetails.toString(), auditUserComment)); + exception.addErrors(DomainUtil.updateDomainDescriptor(original, update, container, user, hasNameChange, changeDetails.toString(), auditUserComment, oldProps, newProps)); QueryService.get().saveCalculatedFieldsMetadata(ListQuerySchema.NAME, update.getQueryName(), hasNameChange ? update.getName() : null, update.getCalculatedFields(), !original.getCalculatedFields().isEmpty(), user, container); } @@ -611,66 +617,47 @@ private ListDomainKindProperties getListProperties(Container container, User use return new TableSelector(ListManager.get().getListMetadataTable(), filter, null).getObject(ListDomainKindProperties.class); } - private String updateListProperties(Container container, User user, int listId, ListDomainKindProperties listProperties) + private Pair, Map> updateListProperties(Container container, User user, int listId, ListDomainKindProperties listProperties) { ListDomainKindProperties existingListProps = getListProperties(container, user, listId); + Map oldProps = existingListProps == null ? null : existingListProps.getAuditRecordMap(); + Map newProps = listProperties == null ? oldProps : listProperties.getAuditRecordMap(); //merge existing and new properties - Pair updatedListProps = updateListProperties(existingListProps, listProperties); + ListDomainKindProperties updatedListProps = updateListProperties(existingListProps, listProperties); - ListManager.get().update(user, container, updatedListProps.first); + ListManager.get().update(user, container, updatedListProps); - return updatedListProps.second; + return new Pair<>(oldProps, newProps); } //updates list properties except listId, domainId, keyName, keyType, and lastIndexed - private Pair updateListProperties(ListDomainKindProperties existingListProps, ListDomainKindProperties newListProps) + private ListDomainKindProperties updateListProperties(ListDomainKindProperties existingListProps, ListDomainKindProperties newListProps) { - StringBuilder changeDetails = new StringBuilder(); ListDomainKindProperties updatedListProps = new ListDomainKindProperties(existingListProps); if (null != newListProps.getName()) - { updatedListProps.setName(newListProps.getName().trim()); - // skip changeDetails for Name since it was previously explicitly added - } - changeDetails.append(DomainUtil.getPropChangeMsg("TitleColumn", existingListProps.getTitleColumn(), newListProps.getTitleColumn())); updatedListProps.setTitleColumn(newListProps.getTitleColumn()); - changeDetails.append(DomainUtil.getPropChangeMsg("Description", existingListProps.getDescription(), newListProps.getDescription())); updatedListProps.setDescription(newListProps.getDescription()); - changeDetails.append(DomainUtil.getPropChangeMsg("AllowDelete", existingListProps.isAllowDelete(), newListProps.isAllowDelete())); updatedListProps.setAllowDelete(newListProps.isAllowDelete()); - changeDetails.append(DomainUtil.getPropChangeMsg("AllowUpload", existingListProps.isAllowUpload(), newListProps.isAllowUpload())); updatedListProps.setAllowUpload(newListProps.isAllowUpload()); - changeDetails.append(DomainUtil.getPropChangeMsg("AllowExport", existingListProps.isAllowExport(), newListProps.isAllowExport())); updatedListProps.setAllowExport(newListProps.isAllowExport()); - changeDetails.append(DomainUtil.getPropChangeMsg("DiscussionSetting", existingListProps.getDiscussionSetting(), newListProps.getDiscussionSetting())); updatedListProps.setDiscussionSetting(newListProps.getDiscussionSetting()); - changeDetails.append(DomainUtil.getPropChangeMsg("Category", existingListProps.getCategory(), newListProps.getCategory())); updatedListProps.setCategory(newListProps.getCategory()); - changeDetails.append(DomainUtil.getPropChangeMsg("EntireListTitleTemplate", existingListProps.getEntireListTitleTemplate(), newListProps.getEntireListTitleTemplate())); updatedListProps.setEntireListTitleTemplate(newListProps.getEntireListTitleTemplate()); - changeDetails.append(DomainUtil.getPropChangeMsg("EntireListIndexSetting", existingListProps.getEntireListIndexSetting(), newListProps.getEntireListIndexSetting())); updatedListProps.setEntireListIndexSetting(newListProps.getEntireListIndexSetting()); - changeDetails.append(DomainUtil.getPropChangeMsg("EntireListBodySetting", existingListProps.getEntireListBodySetting(), newListProps.getEntireListBodySetting())); updatedListProps.setEntireListBodySetting(newListProps.getEntireListBodySetting()); - changeDetails.append(DomainUtil.getPropChangeMsg("EachItemTitleTemplate", existingListProps.getEachItemTitleTemplate(), newListProps.getEachItemTitleTemplate())); updatedListProps.setEachItemTitleTemplate(newListProps.getEachItemTitleTemplate()); - changeDetails.append(DomainUtil.getPropChangeMsg("EachItemBodySetting", existingListProps.getEachItemBodySetting(), newListProps.getEachItemBodySetting())); updatedListProps.setEachItemBodySetting(newListProps.getEachItemBodySetting()); - changeDetails.append(DomainUtil.getPropChangeMsg("EntireListIndex", existingListProps.isEntireListIndex(), newListProps.isEntireListIndex())); updatedListProps.setEntireListIndex(newListProps.isEntireListIndex()); - changeDetails.append(DomainUtil.getPropChangeMsg("EntireListBodyTemplate", existingListProps.getEntireListBodyTemplate(), newListProps.getEntireListBodyTemplate())); updatedListProps.setEntireListBodyTemplate(newListProps.getEntireListBodyTemplate()); - changeDetails.append(DomainUtil.getPropChangeMsg("EachItemIndex", existingListProps.isEachItemIndex(), newListProps.isEachItemIndex())); updatedListProps.setEachItemIndex(newListProps.isEachItemIndex()); - changeDetails.append(DomainUtil.getPropChangeMsg("EachItemBodyTemplate", existingListProps.getEachItemBodyTemplate(), newListProps.getEachItemBodyTemplate())); updatedListProps.setEachItemBodyTemplate(newListProps.getEachItemBodyTemplate()); - changeDetails.append(DomainUtil.getPropChangeMsg("FileAttachmentIndex", existingListProps.isFileAttachmentIndex(), newListProps.isFileAttachmentIndex())); updatedListProps.setFileAttachmentIndex(newListProps.isFileAttachmentIndex()); - return new Pair<>(updatedListProps, changeDetails.toString()); + return updatedListProps; } private GWTPropertyDescriptor findField(String name, List fields) diff --git a/list/src/org/labkey/list/model/ListDomainKindProperties.java b/list/src/org/labkey/list/model/ListDomainKindProperties.java index 71bfb1e6604..9d3a1b82084 100644 --- a/list/src/org/labkey/list/model/ListDomainKindProperties.java +++ b/list/src/org/labkey/list/model/ListDomainKindProperties.java @@ -1,8 +1,11 @@ package org.labkey.list.model; +import org.apache.commons.lang3.StringUtils; import org.labkey.api.exp.list.ListDefinition; import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; /* Java bean used in marshalling and unmarshalling*/ public class ListDomainKindProperties implements Cloneable, ListIndexingSettings @@ -311,4 +314,35 @@ public void setFileAttachmentIndex(boolean fileAttachmentIndex) { this.fileAttachmentIndex = fileAttachmentIndex; } + + public Map getAuditRecordMap() + { + Map map = new LinkedHashMap<>(); + map.put("Name", getName()); + if (!StringUtils.isEmpty(getTitleColumn())) + map.put("TitleColumn", getTitleColumn()); + if (!StringUtils.isEmpty(getDescription())) + map.put("Description", getDescription()); + map.put("AllowDelete", isAllowDelete()); + map.put("AllowUpload", isAllowUpload()); + map.put("AllowExport", isAllowExport()); + map.put("DiscussionSetting", getDiscussionSetting()); + if (!StringUtils.isEmpty(getCategory())) + map.put("Category", getCategory()); + if (!StringUtils.isEmpty(getEntireListTitleTemplate())) + map.put("EntireListTitleTemplate", getEntireListTitleTemplate()); + if (!StringUtils.isEmpty(getEntireListBodyTemplate())) + map.put("EntireListBodyTemplate", getEntireListBodyTemplate()); + if (!StringUtils.isEmpty(getEachItemTitleTemplate())) + map.put("EachItemTitleTemplate", getEachItemTitleTemplate()); + if (!StringUtils.isEmpty(getEachItemBodyTemplate())) + map.put("EachItemBodyTemplate", getEachItemBodyTemplate()); + map.put("EntireListIndexSetting", getEntireListIndexSetting()); + map.put("EntireListBodySetting", getEntireListBodySetting()); + map.put("EachItemBodySetting", getEachItemBodySetting()); + map.put("EntireListIndex", isEntireListIndex()); + map.put("EachItemIndex", isEachItemIndex()); + map.put("FileAttachmentIndex", isFileAttachmentIndex()); + return map; + } } diff --git a/study/src/org/labkey/study/model/DatasetDefinition.java b/study/src/org/labkey/study/model/DatasetDefinition.java index 1cdae2a08a9..3476f268e59 100644 --- a/study/src/org/labkey/study/model/DatasetDefinition.java +++ b/study/src/org/labkey/study/model/DatasetDefinition.java @@ -2824,6 +2824,30 @@ public static void cleanupOrphanedDatasetDomains() } } + public Map getAuditRecordMap() + { + Map map = new LinkedHashMap<>(); + map.put("Name", getName()); + if (!StringUtils.isEmpty(getDescription())) + map.put("Description", getDescription()); + if (!StringUtils.isEmpty(getCategory())) + map.put("Category", getCategory()); + if (!StringUtils.isEmpty(getLabel())) + map.put("Label", getLabel()); + if (!StringUtils.isEmpty(getKeyPropertyName())) + map.put("KeyPropertyName", getKeyPropertyName()); + if (!StringUtils.isEmpty(getVisitDatePropertyName())) + map.put("VisitDateColumnName", getVisitDatePropertyName()); + if (!StringUtils.isEmpty(getTag())) + map.put("Tag", getTag()); + map.put("KeyPropertyManaged", getKeyManagementType() != Dataset.KeyManagementType.None); + map.put("IsDemographicData", isDemographicData()); + map.put("IsUseTimeKeyField", getUseTimeKeyField()); + if (getCohortId() != null) + map.put("CohortId", getCohortId()); + + return map; + } public static class Builder implements org.labkey.api.data.Builder { private final String _name; diff --git a/study/src/org/labkey/study/model/DatasetDomainKind.java b/study/src/org/labkey/study/model/DatasetDomainKind.java index 28722715c2a..8d0efd3dc47 100644 --- a/study/src/org/labkey/study/model/DatasetDomainKind.java +++ b/study/src/org/labkey/study/model/DatasetDomainKind.java @@ -647,12 +647,19 @@ private void checkCanUpdate(DatasetDefinition def, Container container, User use } private @NotNull ValidationException updateDomainDescriptor(GWTDomain original, GWTDomain update, - @Nullable DatasetDefinition oldDef, DatasetDomainKindProperties datasetPropertiesUpdate, Container container, User user, String userComment) + @Nullable DatasetDefinition oldDef, @Nullable DatasetDomainKindProperties datasetPropertiesUpdate, Container container, User user, String userComment) { StringBuilder changeDetails = new StringBuilder(); boolean hasNameChange = false; + Map oldProps = null; + Map newProps = datasetPropertiesUpdate != null ? datasetPropertiesUpdate.getAuditRecordMap() : null; + if (oldDef != null) { + oldProps = oldDef.getAuditRecordMap(); + if (newProps == null) + newProps = oldProps; // no update + hasNameChange = !datasetPropertiesUpdate.getName().equals(oldDef.getName()); if (hasNameChange) changeDetails.append("The name of the dataset '" + oldDef.getName() + "' was changed to '" + datasetPropertiesUpdate.getName() + "'."); @@ -669,7 +676,7 @@ private void checkCanUpdate(DatasetDefinition def, Container container, User use } ValidationException exception = new ValidationException(); - exception.addErrors(DomainUtil.updateDomainDescriptor(original, update, container, user, hasNameChange, changeDetails.toString(), userComment)); + exception.addErrors(DomainUtil.updateDomainDescriptor(original, update, container, user, hasNameChange, changeDetails.toString(), userComment, oldProps, newProps)); return exception; } @@ -734,7 +741,7 @@ private ValidationException updateDataset(DatasetDomainKindProperties datasetPro @Override public @NotNull ValidationException updateDomain(GWTDomain original, GWTDomain update, - DatasetDomainKindProperties datasetProperties, Container container, User user, boolean includeWarnings, String userComment) + @Nullable DatasetDomainKindProperties datasetProperties, Container container, User user, boolean includeWarnings, String userComment) { assert original.getDomainURI().equals(update.getDomainURI()); StudyImpl study = StudyManager.getInstance().getStudy(container); diff --git a/study/src/org/labkey/study/model/DatasetDomainKindProperties.java b/study/src/org/labkey/study/model/DatasetDomainKindProperties.java index 402a7f0c6d8..32cef7cf4fa 100644 --- a/study/src/org/labkey/study/model/DatasetDomainKindProperties.java +++ b/study/src/org/labkey/study/model/DatasetDomainKindProperties.java @@ -1,11 +1,15 @@ package org.labkey.study.model; +import org.apache.commons.lang3.StringUtils; import org.labkey.api.data.Container; import org.labkey.api.exp.api.ExpObject; import org.labkey.api.study.Dataset; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; +import java.util.LinkedHashMap; +import java.util.Map; + public class DatasetDomainKindProperties implements Cloneable { private String _entityId; @@ -330,4 +334,29 @@ public void setStrictFieldValidation(boolean strictFieldValidation) { _strictFieldValidation = strictFieldValidation; } + + public Map getAuditRecordMap() + { + Map map = new LinkedHashMap<>(); + map.put("Name", getName()); + if (!StringUtils.isEmpty(getDescription())) + map.put("Description", getDescription()); + if (!StringUtils.isEmpty(getCategory())) + map.put("Category", getCategory()); + if (!StringUtils.isEmpty(getLabel())) + map.put("Label", getLabel()); + if (!StringUtils.isEmpty(getKeyPropertyName())) + map.put("KeyPropertyName", getKeyPropertyName()); + if (!StringUtils.isEmpty(getVisitDatePropertyName())) + map.put("VisitDateColumnName", getVisitDatePropertyName()); + if (!StringUtils.isEmpty(getTag())) + map.put("Tag", getTag()); + map.put("KeyPropertyManaged", isKeyPropertyManaged()); + map.put("IsDemographicData", isDemographicData()); + map.put("IsUseTimeKeyField", isUseTimeKeyField()); + if (getCohortId() != null) + map.put("CohortId", getCohortId()); + + return map; + } } From 485de766e249e961069f9c0ba7a53850766b7506 Mon Sep 17 00:00:00 2001 From: XingY Date: Fri, 16 May 2025 15:16:41 -0700 Subject: [PATCH 15/30] populate new record map during domain creation --- .../gwt/client/assay/model/GWTProtocol.java | 26 ++++++++ .../labkey/api/gwt/client/model/GWTIndex.java | 11 ++-- .../api/assay/AbstractAssayProvider.java | 5 +- .../org/labkey/api/assay/AssayProvider.java | 3 +- .../labkey/api/data/PropertyStorageSpec.java | 9 +++ .../org/labkey/api/exp/api/ExpProtocol.java | 3 + .../api/exp/api/SampleTypeDomainKind.java | 2 +- .../labkey/api/exp/api/SampleTypeService.java | 2 +- .../labkey/api/exp/list/ListDefinition.java | 3 +- .../org/labkey/api/exp/property/Domain.java | 2 +- .../labkey/api/exp/property/DomainUtil.java | 10 +-- .../labkey/assay/AssayDomainServiceImpl.java | 61 +++++++++++-------- .../experiment/api/ExpProtocolImpl.java | 34 +++++++++++ .../experiment/api/ExperimentServiceImpl.java | 2 +- .../experiment/api/SampleTypeServiceImpl.java | 6 +- .../experiment/api/property/DomainImpl.java | 17 ++++-- .../api/property/StorageProvisionerImpl.java | 2 +- .../labkey/list/model/ListDefinitionImpl.java | 6 +- .../org/labkey/list/model/ListDomainKind.java | 2 +- .../labkey/query/QueryServiceImplTestCase.jsp | 2 +- .../labkey/study/model/DatasetDomainKind.java | 12 +--- 21 files changed, 152 insertions(+), 68 deletions(-) diff --git a/api/gwtsrc/org/labkey/api/gwt/client/assay/model/GWTProtocol.java b/api/gwtsrc/org/labkey/api/gwt/client/assay/model/GWTProtocol.java index 8d94f46b7db..2fed56970d3 100644 --- a/api/gwtsrc/org/labkey/api/gwt/client/assay/model/GWTProtocol.java +++ b/api/gwtsrc/org/labkey/api/gwt/client/assay/model/GWTProtocol.java @@ -17,12 +17,14 @@ package org.labkey.api.gwt.client.assay.model; import com.google.gwt.user.client.rpc.IsSerializable; +import org.apache.commons.lang3.StringUtils; import org.labkey.api.gwt.client.model.GWTContainer; import org.labkey.api.gwt.client.model.GWTDomain; import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -448,4 +450,28 @@ public void setAuditUserComment(String auditUserComment) _auditUserComment = auditUserComment; } + public Map getAuditRecordMap() + { + Map map = new LinkedHashMap<>(); + map.put("Name", getName()); + if (!StringUtils.isEmpty(getDescription())) + map.put("Description", getDescription()); + if (!StringUtils.isEmpty(getStatus())) + map.put("Status", getStatus()); + String autoCopyTargetContainerId = getAutoCopyTargetContainer() != null ? getAutoCopyTargetContainer().getEntityId() : getAutoCopyTargetContainerId(); + if (!StringUtils.isEmpty(autoCopyTargetContainerId)) + map.put("AutoCopyTargetContainer", autoCopyTargetContainerId); + if (!StringUtils.isEmpty(getAutoLinkCategory())) + map.put("AutoLinkCategory", getAutoLinkCategory()); + map.put("SaveScriptFiles", isSaveScriptFiles()); + map.put("IsEditableResults", isEditableResults()); + map.put("IsEditableRuns", isEditableRuns()); + map.put("IsBackgroundUpload", isBackgroundUpload()); + map.put("IsQcEnabled", isQcEnabled()); + map.put("IsPlateMetadataEnabled", isPlateMetadata()); + + return map; + } + + } diff --git a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java index 4c7e7eb9147..a765bebf341 100644 --- a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java +++ b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java @@ -17,12 +17,14 @@ import com.google.gwt.user.client.rpc.IsSerializable; import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.exp.property.DomainUtil; +import org.labkey.api.data.PropertyStorageSpec; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * User: kevink @@ -81,12 +83,13 @@ public String toStringVal() return StringUtils.join(_columnNames, ", ") + ", unique: " + isUnique(); } - public static List toStringVals(List indices) + public static List toStringVals(List indices, Set excludeBaseIndices) { if (indices == null || indices.isEmpty()) return null; - return indices.stream().map(GWTIndex::toStringVal).toList(); + Set excludeIndices = excludeBaseIndices == null ? Collections.emptySet() : excludeBaseIndices.stream().map(PropertyStorageSpec.Index::toStringVal).collect(Collectors.toSet()); + return indices.stream().map(GWTIndex::toStringVal).filter(v -> !excludeIndices.contains(v)).toList(); } } diff --git a/api/src/org/labkey/api/assay/AbstractAssayProvider.java b/api/src/org/labkey/api/assay/AbstractAssayProvider.java index de6343ecbe1..a4bf95f51c7 100644 --- a/api/src/org/labkey/api/assay/AbstractAssayProvider.java +++ b/api/src/org/labkey/api/assay/AbstractAssayProvider.java @@ -1247,7 +1247,7 @@ public boolean hasUsefulDetailsPage() private static final String SCRIPT_PATH_DELIMITER = "|"; @Override - public Pair setValidationAndAnalysisScripts(ExpProtocol protocol, @NotNull List scripts) throws ExperimentException + public Pair> setValidationAndAnalysisScripts(ExpProtocol protocol, @NotNull List scripts) throws ExperimentException { Map props = new HashMap<>(protocol.getObjectProperties()); String propertyURI = ScriptType.TRANSFORM.getPropertyURI(protocol); @@ -1296,7 +1296,6 @@ public Pair setValidationAndAnalysisScripts(ExpProt JSONArray json = AnalysisScript.toJson(scripts); ObjectProperty oldProp = props.get(propertyURI); String oldJson = oldProp == null ? null : oldProp.getStringValue(); - String auditMsg = DomainUtil.getPropChangeMsg("TransformScript", oldJson, json); if (json != null) { ObjectProperty prop = new ObjectProperty(protocol.getLSID(), protocol.getContainer(), @@ -1309,7 +1308,7 @@ public Pair setValidationAndAnalysisScripts(ExpProt } protocol.setObjectProperties(props); - return new Pair<>(validationErrors, auditMsg); + return new Pair<>(validationErrors, new Pair<>(oldJson, json == null ? null : json.toString())); } /** For migrating legacy assay designs that have separate transform and validation script properties */ diff --git a/api/src/org/labkey/api/assay/AssayProvider.java b/api/src/org/labkey/api/assay/AssayProvider.java index 4546eb6bc08..925d74281ca 100644 --- a/api/src/org/labkey/api/assay/AssayProvider.java +++ b/api/src/org/labkey/api/assay/AssayProvider.java @@ -239,8 +239,9 @@ enum Scope /** * File based QC and analysis scripts can be added to a protocol and invoked when the validate * method is called. Set to an empty list if no scripts exist. + * @return ValidationException, a pair of old and new string representation of the script description (for audit use) */ - Pair setValidationAndAnalysisScripts(ExpProtocol protocol, @NotNull List scripts) throws ExperimentException; + Pair> setValidationAndAnalysisScripts(ExpProtocol protocol, @NotNull List scripts) throws ExperimentException; @NotNull List getValidationAndAnalysisScripts(ExpProtocol protocol, Scope scope); diff --git a/api/src/org/labkey/api/data/PropertyStorageSpec.java b/api/src/org/labkey/api/data/PropertyStorageSpec.java index 7ed40b552df..3ea16a85024 100644 --- a/api/src/org/labkey/api/data/PropertyStorageSpec.java +++ b/api/src/org/labkey/api/data/PropertyStorageSpec.java @@ -15,6 +15,7 @@ */ package org.labkey.api.data; +import org.apache.commons.lang3.StringUtils; import org.labkey.api.collections.CaseInsensitiveHashSet; import org.labkey.api.exp.MvColumn; import org.labkey.api.exp.PropertyDescriptor; @@ -500,6 +501,14 @@ public int hashCode() { return Objects.hash(Arrays.hashCode(columnNames), isClustered, isClustered); } + + public String toStringVal() + { + if (columnNames == null) + return ""; + + return StringUtils.join(columnNames, ", ") + ", unique: " + isUnique; + } } public enum Special diff --git a/api/src/org/labkey/api/exp/api/ExpProtocol.java b/api/src/org/labkey/api/exp/api/ExpProtocol.java index d726d34e2c2..ae60d93bede 100644 --- a/api/src/org/labkey/api/exp/api/ExpProtocol.java +++ b/api/src/org/labkey/api/exp/api/ExpProtocol.java @@ -18,6 +18,7 @@ import org.jetbrains.annotations.Nullable; import org.labkey.api.assay.AbstractAssayProvider; +import org.labkey.api.assay.AssayProvider; import org.labkey.api.data.Container; import org.labkey.api.exp.ObjectProperty; import org.labkey.api.exp.ProtocolParameter; @@ -178,4 +179,6 @@ static boolean isSampleWorkflowProtocol(String lsid) { return isSampleWorkflowTaskProtocol(lsid) || isSampleWorkflowJobProtocol(lsid); } + + Map getAuditRecordMap(AssayProvider provider, Container container); } diff --git a/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java b/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java index f978bc1b581..8fdef0f069e 100644 --- a/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java +++ b/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java @@ -592,7 +592,7 @@ public Domain createDomain(GWTDomain domain, @Nullable SampleTypeDomainKindPrope try { st = SampleTypeService.get().createSampleType(container, user, name, description, properties, indices, idCol1, idCol2, idCol3, parentCol, nameExpression, aliquotNameExpression, - templateInfo, aliases, labelColor, metricUnit, autoLinkTargetContainer, autoLinkCategory, category, domain.getDisabledSystemFields(), excludedContainerIds, excludedDashboardContainerIds); + templateInfo, aliases, labelColor, metricUnit, autoLinkTargetContainer, autoLinkCategory, category, domain.getDisabledSystemFields(), excludedContainerIds, excludedDashboardContainerIds, arguments != null ? arguments.getAuditRecordMap() : null); } catch (SQLException e) { diff --git a/api/src/org/labkey/api/exp/api/SampleTypeService.java b/api/src/org/labkey/api/exp/api/SampleTypeService.java index 9e2877249d8..2d3bc26ecff 100644 --- a/api/src/org/labkey/api/exp/api/SampleTypeService.java +++ b/api/src/org/labkey/api/exp/api/SampleTypeService.java @@ -139,7 +139,7 @@ ExpSampleType createSampleType(Container container, User user, String name, Stri ExpSampleType createSampleType(Container c, User u, String name, String description, List properties, List indices, int idCol1, int idCol2, int idCol3, int parentCol, String nameExpression, String aliquotNameExpression, @Nullable TemplateInfo templateInfo, @Nullable Map> importAliases, @Nullable String labelColor, @Nullable String metricUnit, @Nullable Container autoLinkTargetContainer, @Nullable String autoLinkCategory, @Nullable String category, @Nullable List disabledSystemField, - @Nullable List excludedContainerIds, @Nullable List excludedDashboardContainerIds) + @Nullable List excludedContainerIds, @Nullable List excludedDashboardContainerIds, @Nullable Map changeDetails) throws ExperimentException, SQLException; @NotNull diff --git a/api/src/org/labkey/api/exp/list/ListDefinition.java b/api/src/org/labkey/api/exp/list/ListDefinition.java index 89ca4c58290..81843a110fe 100644 --- a/api/src/org/labkey/api/exp/list/ListDefinition.java +++ b/api/src/org/labkey/api/exp/list/ListDefinition.java @@ -39,6 +39,7 @@ import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.Map; /** * Represents a single list definition, as captured by a domain and some list-specific configuration, and defined @@ -290,7 +291,7 @@ public static BodySetting getForValue(int value) void setKeyType(KeyType type); void save(User user) throws Exception; - void save(User user, boolean ensureKey) throws Exception; + void save(User user, boolean ensureKey, @Nullable Map newRecordMap) throws Exception; void delete(User user) throws DomainNotFoundException; void delete(User user, @Nullable String auditUserComment) throws DomainNotFoundException; diff --git a/api/src/org/labkey/api/exp/property/Domain.java b/api/src/org/labkey/api/exp/property/Domain.java index 9e62df3dbf5..653b3c86b3b 100644 --- a/api/src/org/labkey/api/exp/property/Domain.java +++ b/api/src/org/labkey/api/exp/property/Domain.java @@ -88,7 +88,7 @@ default void delete(@Nullable User user, @Nullable String auditUserComment) thro delete(user); } void save(User user) throws ChangePropertyDescriptorException; - void save(User user, boolean allowAddBaseProperty) throws ChangePropertyDescriptorException; + void save(User user, @Nullable Map newRecordMap) throws ChangePropertyDescriptorException; void save(User user, @Nullable String auditComment, @Nullable String auditUserComment, @Nullable Map oldRecordMap, @Nullable Map newRecordMap) throws ChangePropertyDescriptorException; /** Returns true if this domain has not yet been saved. */ diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index 69e4ea74484..833fe7c8b28 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -34,6 +34,7 @@ import org.labkey.api.data.ContainerService; import org.labkey.api.data.NameGenerator; import org.labkey.api.data.PHI; +import org.labkey.api.data.PropertyStorageSpec; import org.labkey.api.data.SchemaTableInfo; import org.labkey.api.data.SimpleFilter; import org.labkey.api.data.TableInfo; @@ -854,15 +855,16 @@ public static ValidationException updateDomainDescriptor(GWTDomain oldIndices = GWTIndex.toStringVals(orig.getIndices()); - if (oldIndices != null) + Set baseIndices = kind.getPropertyIndices(d); + List oldIndices = GWTIndex.toStringVals(orig.getIndices(), baseIndices); + if (oldIndices != null && !oldIndices.isEmpty()) { if (oldProps == null) oldProps = new LinkedHashMap<>(); oldProps.put("Indices", oldIndices); } - List newIndices = GWTIndex.toStringVals(update.getIndices()); - if (newIndices != null) + List newIndices = GWTIndex.toStringVals(update.getIndices(), baseIndices); + if (newIndices != null && !newIndices.isEmpty()) { if (newProps == null) newProps = new LinkedHashMap<>(); diff --git a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java index 25a376beeaf..69aa53f1ca0 100644 --- a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java +++ b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java @@ -79,6 +79,7 @@ import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -416,6 +417,9 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr if (assay.getAutoLinkCategory() != null && assay.getAutoLinkCategory().length() > 200) throw new ValidationException("Linked Dataset Category name must be shorter than 200 characters."); + Map newProps = assay.getAuditRecordMap(); + Map oldProps = new LinkedHashMap<>(); + StringBuilder changeDetails = new StringBuilder(); ExpProtocol protocol; boolean isNew = assay.getProtocolId() == null; @@ -452,7 +456,7 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr else { GWTDomain previous = DomainUtil.getDomainDescriptor(getUser(), domain.getDomainURI(), protocol.getContainer()); - updateDomainDescriptor(assayProvider, protocol, previous, domain, false, null); + updateDomainDescriptor(assayProvider, protocol, previous, domain, false, null, assay.getAuditUserComment(), oldProps, newProps); domainURIs.add(domain.getDomainURI()); } } @@ -465,6 +469,8 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr if (protocol == null) throw new ValidationException("Assay design has been deleted"); + oldProps = protocol.getAuditRecordMap(AssayService.get().getProvider(protocol), getContainer()); + // ensure that the user has edit perms in this container if (!canUpdateProtocols()) throw new ValidationException("You do not have sufficient permissions to update this Assay"); @@ -482,9 +488,7 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr throw new ValidationException(nameError); } protocol.setName(assay.getName()); - changeDetails.append(DomainUtil.getStringPropChangeMsg("Description", protocol.getProtocolDescription(), assay.getDescription())); protocol.setProtocolDescription(assay.getDescription()); - changeDetails.append(DomainUtil.getStringPropChangeMsg("Status", protocol.getStatus().toString(), assay.getStatus())); if (assay.getStatus() != null) protocol.setStatus(ExpProtocol.Status.valueOf(assay.getStatus())); } @@ -560,16 +564,27 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr if (detectionMethod == null) throw new ValidationException("The selected detection method could not be found."); - String oldDetectionMethod = dmProvider.getSelectedDetectionMethod(getContainer(), protocol); - changeDetails.append(DomainUtil.getPropChangeMsg("DetectionMethod", oldDetectionMethod, detectionMethod)); + if (!isNew) + { + String oldDetectionMethod = dmProvider.getSelectedDetectionMethod(getContainer(), protocol); + if (oldDetectionMethod != null && !StringUtils.isEmpty(oldDetectionMethod)) + oldProps.put("DetectionMethod", oldDetectionMethod); + } + if (!StringUtils.isEmpty(detectionMethod)) + newProps.put("DetectionMethod", detectionMethod); dmProvider.setSelectedDetectionMethod(getContainer(), protocol, detectionMethod); } - Pair scriptValidationResult = provider.setValidationAndAnalysisScripts(protocol, transformScripts); + Pair> scriptValidationResult = provider.setValidationAndAnalysisScripts(protocol, transformScripts); ValidationException scriptValidation = scriptValidationResult.first; - String transformScriptChangeMsg = scriptValidationResult.second; - if (transformScriptChangeMsg != null) - changeDetails.append(transformScriptChangeMsg); + Pair transformChanges = scriptValidationResult.second; + if (transformChanges != null) + { + if (!isNew && !StringUtils.isEmpty(transformChanges.first)) + oldProps.put("TransformScripts", transformChanges.first); + if (!StringUtils.isEmpty(transformChanges.second)) + newProps.put("TransformScripts", transformChanges.second); + } if (scriptValidation.hasErrors()) { for (var error : scriptValidation.getErrors()) @@ -584,17 +599,11 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr } } - changeDetails.append(DomainUtil.getPropChangeMsg("SaveScriptFiles", provider.isSaveScriptFiles(protocol), assay.isSaveScriptFiles())); provider.setSaveScriptFiles(protocol, assay.isSaveScriptFiles()); - changeDetails.append(DomainUtil.getPropChangeMsg("IsEditableResults", provider.isEditableResults(protocol), assay.isEditableResults())); provider.setEditableResults(protocol, assay.isEditableResults()); - changeDetails.append(DomainUtil.getPropChangeMsg("IsEditableRuns", provider.isEditableRuns(protocol), assay.isEditableRuns())); provider.setEditableRuns(protocol, assay.isEditableRuns()); - changeDetails.append(DomainUtil.getPropChangeMsg("IsBackgroundUpload", provider.isBackgroundUpload(protocol), assay.isBackgroundUpload())); provider.setBackgroundUpload(protocol, assay.isBackgroundUpload()); - changeDetails.append(DomainUtil.getPropChangeMsg("IsQcEnabled", provider.isQCEnabled(protocol), assay.isQcEnabled())); provider.setQCEnabled(protocol, assay.isQcEnabled()); - changeDetails.append(DomainUtil.getPropChangeMsg("IsPlateMetadataEnabled", provider.isPlateMetadataEnabled(protocol), assay.isPlateMetadata())); provider.setPlateMetadataEnabled(protocol, assay.isPlateMetadata()); Map props = new HashMap<>(protocol.getObjectProperties()); @@ -606,18 +615,12 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr throw new ValidationException("No such auto-link target container id: " + autoLinkTargetContainerId); } - ObjectProperty oldAutoLinkTargetContainer = props.get(StudyPublishService.AUTO_LINK_TARGET_PROPERTY_URI); - String oldAutoLinkTargetContainerId = oldAutoLinkTargetContainer == null ? null : oldAutoLinkTargetContainer.getStringValue(); - changeDetails.append(DomainUtil.getPropChangeMsg("AutoCopyTargetContainer", oldAutoLinkTargetContainerId, autoLinkTargetContainerId)); if (autoLinkTargetContainerId != null) props.put(StudyPublishService.AUTO_LINK_TARGET_PROPERTY_URI, new ObjectProperty(protocol.getLSID(), protocol.getContainer(), StudyPublishService.AUTO_LINK_TARGET_PROPERTY_URI, autoLinkTargetContainerId)); else props.remove(StudyPublishService.AUTO_LINK_TARGET_PROPERTY_URI); String autoLinkCategory = assay.getAutoLinkCategory(); - ObjectProperty oldAutoLinkCategory = props.get(StudyPublishService.AUTO_LINK_CATEGORY_PROPERTY_URI); - String oldAutoLinkCategoryValue = oldAutoLinkCategory == null ? null : oldAutoLinkCategory.getStringValue(); - changeDetails.append(DomainUtil.getPropChangeMsg("AutoLinkCategory", oldAutoLinkCategoryValue, autoLinkCategory)); if (autoLinkCategory != null) props.put(StudyPublishService.AUTO_LINK_CATEGORY_PROPERTY_URI, new ObjectProperty(protocol.getLSID(), protocol.getContainer(), StudyPublishService.AUTO_LINK_CATEGORY_PROPERTY_URI, autoLinkCategory)); else @@ -628,14 +631,19 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr protocol.save(getUser()); if (assay.getExcludedContainerIds() != null && (!isNew || !assay.getExcludedContainerIds().isEmpty())) - changeDetails.append(ExperimentService.get().ensureDataTypeContainerExclusions(ExperimentService.DataTypeForExclusion.AssayDesign, assay.getExcludedContainerIds(), protocol.getRowId(), getUser())); + { + Pair, Collection> exclusionChanges = ExperimentService.get().ensureDataTypeContainerExclusions(ExperimentService.DataTypeForExclusion.AssayDesign, assay.getExcludedContainerIds(), protocol.getRowId(), getUser()); + if (!isNew) + oldProps.put("ContainerExclusions", exclusionChanges.first); + newProps.put("ContainerExclusions", exclusionChanges.second); + } else ExperimentService.get().ensureDataTypeContainerExclusionsNonAdmin(ExperimentService.DataTypeForExclusion.AssayDesign, protocol.getRowId(), getContainer(), getUser()); for (GWTDomain domain : assay.getDomains()) { GWTDomain previous = DomainUtil.getDomainDescriptor(getUser(), domain.getDomainURI(), protocol.getContainer()); - updateDomainDescriptor(provider, protocol, previous, domain, hasNameChange, changeDetails.toString()); + updateDomainDescriptor(provider, protocol, previous, domain, hasNameChange, changeDetails.toString(), assay.getAuditUserComment(), oldProps, newProps); boolean hasExistingCalcFields = previous != null && !previous.getCalculatedFields().isEmpty(); GWTDomain savedDomain = DomainUtil.getDomainDescriptor(getUser(), domain.getDomainURI(), protocol.getContainer()); @@ -665,7 +673,10 @@ private void updateDomainDescriptor( GWTDomain original, GWTDomain update, boolean hasNameChange, - String auditComment + String auditComment, + @Nullable String auditUserComment, + @Nullable Map oldRecordMap, + @Nullable Map newRecordMap ) throws ValidationException { for (GWTPropertyDescriptor prop : update.getFields()) @@ -678,7 +689,7 @@ private void updateDomainDescriptor( provider.beforeDomainChange(getUser(), protocol, original, update); // Update - ValidationException validationErrors = DomainUtil.updateDomainDescriptor(original, update, getContainer(), getUser(), hasNameChange, auditComment); + ValidationException validationErrors = DomainUtil.updateDomainDescriptor(original, update, getContainer(), getUser(), hasNameChange, auditComment, auditUserComment, oldRecordMap, newRecordMap); if (validationErrors.hasErrors()) throw validationErrors; diff --git a/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java b/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java index 9d8507397b9..4585201aa0d 100644 --- a/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java @@ -16,7 +16,9 @@ package org.labkey.experiment.api; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; +import org.labkey.api.assay.AssayProvider; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; import org.labkey.api.data.Filter; @@ -44,12 +46,15 @@ import org.labkey.api.query.QueryRowReference; import org.labkey.api.query.RuntimeValidationException; import org.labkey.api.security.User; +import org.labkey.api.study.publish.StudyPublishService; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -475,4 +480,33 @@ public void setStatus(Status status) { _object.setStatus(status); } + + @Override + public Map getAuditRecordMap(AssayProvider provider, Container container) + { + Map map = new LinkedHashMap<>(); + map.put("Name", getName()); + if (!StringUtils.isEmpty(getProtocolDescription())) + map.put("Description", getProtocolDescription()); + if (null != getStatus()) + map.put("Status", getStatus().toString()); + Map props = new HashMap<>(getObjectProperties()); + ObjectProperty autoLinkTargetContainer = props.get(StudyPublishService.AUTO_LINK_TARGET_PROPERTY_URI); + String autoLinkTargetContainerId = autoLinkTargetContainer == null ? null : autoLinkTargetContainer.getStringValue(); + if (!StringUtils.isEmpty(autoLinkTargetContainerId)) + map.put("AutoCopyTargetContainer", autoLinkTargetContainerId); + ObjectProperty autoLinkCategory = props.get(StudyPublishService.AUTO_LINK_CATEGORY_PROPERTY_URI); + String autoLinkCategoryValue = autoLinkCategory == null ? null : autoLinkCategory.getStringValue(); + if (!StringUtils.isEmpty(autoLinkCategoryValue)) + map.put("AutoLinkCategory", autoLinkCategoryValue); + map.put("SaveScriptFiles", provider.isSaveScriptFiles(this)); + map.put("IsEditableResults", provider.isEditableResults(this)); + map.put("IsEditableRuns", provider.isEditableRuns(this)); + map.put("IsBackgroundUpload", provider.isBackgroundUpload(this)); + map.put("IsQcEnabled", provider.isQCEnabled(this)); + map.put("IsPlateMetadataEnabled", provider.isPlateMetadataEnabled(this)); + return map; + } + + } diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index 3c909f21082..c00557cd07b 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -7939,7 +7939,7 @@ else if (domain.getPropertyByName(propertyName) != null) // issue 25275 if (kind != null) domain.setPropertyForeignKeys(kind.getPropertyForeignKeys(c)); - domain.save(u); + domain.save(u, options == null ? null : options.getAuditRecordMap()); impl.save(u); SchemaKey schemaKey = SchemaKey.fromParts(ExpSchema.SCHEMA_NAME, DataClassUserSchema.NAME); diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index 62b20dbc731..f374af671c4 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -699,7 +699,7 @@ public ExpSampleTypeImpl createSampleType(Container c, User u, String name, Stri public ExpSampleTypeImpl createSampleType(Container c, User u, String name, String description, List properties, List indices, int idCol1, int idCol2, int idCol3, int parentCol, String nameExpression, String aliquotNameExpression, @Nullable TemplateInfo templateInfo, @Nullable Map> importAliases, @Nullable String labelColor, @Nullable String metricUnit) throws ExperimentException { - return createSampleType(c, u, name, description, properties, indices, idCol1, idCol2, idCol3, parentCol, nameExpression, aliquotNameExpression, templateInfo, importAliases, labelColor, metricUnit, null, null, null, null, null, null); + return createSampleType(c, u, name, description, properties, indices, idCol1, idCol2, idCol3, parentCol, nameExpression, aliquotNameExpression, templateInfo, importAliases, labelColor, metricUnit, null, null, null, null, null, null, null); } @NotNull @@ -707,7 +707,7 @@ public ExpSampleTypeImpl createSampleType(Container c, User u, String name, Stri public ExpSampleTypeImpl createSampleType(Container c, User u, String name, String description, List properties, List indices, int idCol1, int idCol2, int idCol3, int parentCol, String nameExpression, String aliquotNameExpression, @Nullable TemplateInfo templateInfo, @Nullable Map> importAliases, @Nullable String labelColor, @Nullable String metricUnit, @Nullable Container autoLinkTargetContainer, @Nullable String autoLinkCategory, @Nullable String category, @Nullable List disabledSystemField, - @Nullable List excludedContainerIds, @Nullable List excludedDashboardContainerIds) + @Nullable List excludedContainerIds, @Nullable List excludedDashboardContainerIds, @Nullable Map changeDetails) throws ExperimentException { validateSampleTypeName(c, u, name, false); @@ -880,7 +880,7 @@ public ExpSampleTypeImpl createSampleType(Container c, User u, String name, Stri { try { - domain.save(u); + domain.save(u, changeDetails); st.save(u); QueryService.get().saveCalculatedFieldsMetadata(SamplesSchema.SCHEMA_NAME, name, null, calculatedFields, false, u, c); DefaultValueService.get().setDefaultValues(domain.getContainer(), defaultValues); diff --git a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java index 7c945a00a3a..6abc6689d76 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java @@ -379,7 +379,7 @@ public boolean isNew() @Override public void save(User user) throws ChangePropertyDescriptorException { - save(user, false); + save(user, null); } @Override @@ -529,9 +529,9 @@ public void saveIfNotExists(User user) throws ChangePropertyDescriptorException } @Override - public void save(User user, boolean allowAddBaseProperty) throws ChangePropertyDescriptorException + public void save(User user, @Nullable Map newRecordMap) throws ChangePropertyDescriptorException { - save(user, false, false, null, null, null, null); + save(user, false, false, null, null, null, newRecordMap); } @Override @@ -820,17 +820,22 @@ else if (null != pdOld) // Move audit event creation to outside the transaction to avoid deadlocks involving audit storage table creation Runnable afterDomainCommit = () -> { + Long newDomainEventId = null; + String columnModifiedMsg = String.format("The column(s) of domain %s were modified.", _dd.getName()); if (isDomainNew) - addAuditEvent(user, extraAuditComment + String.format("The domain %s was created", _dd.getName()), auditUserComment, getContainer(), oldRecordMap, newRecordMap); + { + String columnMsg_ = finalPropChanged ? " " + columnModifiedMsg : ""; + newDomainEventId = addAuditEvent(user, extraAuditComment + String.format("The domain %s was created.", _dd.getName()) + columnMsg_, auditUserComment, getContainer(), oldRecordMap, newRecordMap); + } if (finalPropChanged) { - final Long domainEventId = addAuditEvent(user, extraAuditComment + String.format("The column(s) of domain %s were modified", _dd.getName()), auditUserComment, getContainer(), oldRecordMap, newRecordMap); + final Long domainEventId = newDomainEventId != null ? newDomainEventId : addAuditEvent(user, extraAuditComment + columnModifiedMsg, auditUserComment, getContainer(), oldRecordMap, newRecordMap); propertyAuditInfo.forEach(auditInfo -> addPropertyAuditEvent(user, getContainer(), auditInfo.getProp(), auditInfo.getAction(), domainEventId, getName(), auditInfo.getDetails())); } else if (!isDomainNew) { - addAuditEvent(user, extraAuditComment + String.format("The descriptor of domain %s was updated", _dd.getName()), auditUserComment, getContainer(), oldRecordMap, newRecordMap); + addAuditEvent(user, extraAuditComment + String.format("The descriptor of domain %s was updated.", _dd.getName()), auditUserComment, getContainer(), oldRecordMap, newRecordMap); } }; transaction.addCommitTask(afterDomainCommit, DbScope.CommitTaskOption.POSTCOMMIT); diff --git a/experiment/src/org/labkey/experiment/api/property/StorageProvisionerImpl.java b/experiment/src/org/labkey/experiment/api/property/StorageProvisionerImpl.java index 43fda173531..71062667db2 100644 --- a/experiment/src/org/labkey/experiment/api/property/StorageProvisionerImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/StorageProvisionerImpl.java @@ -1814,7 +1814,7 @@ public DomainKind getDomainKind() return k; } }; - d.save(user, false); + d.save(user); StorageProvisioner.get().ensureStorageTable(d, k, k.getScope()); // check that prop exists diff --git a/list/src/org/labkey/list/model/ListDefinitionImpl.java b/list/src/org/labkey/list/model/ListDefinitionImpl.java index 6ac2f0f61c0..ef6155d1116 100644 --- a/list/src/org/labkey/list/model/ListDefinitionImpl.java +++ b/list/src/org/labkey/list/model/ListDefinitionImpl.java @@ -370,13 +370,13 @@ public void setFileAttachmentIndex(boolean fileAttachmentIndex) @Override public void save(User user) throws Exception { - save(user, true); + save(user, true, null); } private static final ReentrantLock _saveLock = new ReentrantLockWithName(ListDefinitionImpl.class, "_saveLock"); @Override - public void save(User user, boolean ensureKey) throws Exception + public void save(User user, boolean ensureKey, @Nullable Map newRecordMap) throws Exception { if (ensureKey) { @@ -396,7 +396,7 @@ public void save(User user, boolean ensureKey) throws Exception // The domain kind cannot lookup the list definition if the domain has not been saved ((ListDomainKind) domain.getDomainKind()).setListDefinition(this); - domain.save(user); + domain.save(user, newRecordMap); _def.setDomainId(domain.getTypeId()); ListDef inserted = ListManager.get().insert(user, _def, _preferredListIds); diff --git a/list/src/org/labkey/list/model/ListDomainKind.java b/list/src/org/labkey/list/model/ListDomainKind.java index 5999f465250..e9906e520e7 100644 --- a/list/src/org/labkey/list/model/ListDomainKind.java +++ b/list/src/org/labkey/list/model/ListDomainKind.java @@ -436,7 +436,7 @@ public Domain createDomain(GWTDomain domain, ListDomainKindProperties listProper // TODO: This looks like the wrong order to me -- we should updateListProperties() (persist the indexing // settings and handle potential transitions) before calling save() (which indexes the list). Since this is // the create case there's no data to index, but there is meta data... - list.save(user); + list.save(user, true, listProperties.getAuditRecordMap()); updateListProperties(container, user, list.getListId(), listProperties); QueryService.get().saveCalculatedFieldsMetadata(ListQuerySchema.NAME, name, null, domain.getCalculatedFields(), false, user, container); diff --git a/query/src/org/labkey/query/QueryServiceImplTestCase.jsp b/query/src/org/labkey/query/QueryServiceImplTestCase.jsp index 54a728e7834..d93b1a2a223 100644 --- a/query/src/org/labkey/query/QueryServiceImplTestCase.jsp +++ b/query/src/org/labkey/query/QueryServiceImplTestCase.jsp @@ -142,7 +142,7 @@ d.addProperty(new PropertyStorageSpec("A", JdbcType.INTEGER)); d.addProperty(new PropertyStorageSpec("B", JdbcType.INTEGER)); d.addProperty(new PropertyStorageSpec("C", JdbcType.INTEGER)); - list.save(user,true); + list.save(user,true, null); } diff --git a/study/src/org/labkey/study/model/DatasetDomainKind.java b/study/src/org/labkey/study/model/DatasetDomainKind.java index 8d0efd3dc47..4cadd7b911d 100644 --- a/study/src/org/labkey/study/model/DatasetDomainKind.java +++ b/study/src/org/labkey/study/model/DatasetDomainKind.java @@ -514,7 +514,7 @@ else if (!timepointType.isVisitBased() && getKindName().equals(VisitDatasetDomai DomainUtil.addProperty(newDomain, pd, defaultValues, propertyUris, null); } - newDomain.save(user); + newDomain.save(user, arguments.getAuditRecordMap()); List indices = (List)domain.getIndices(); newDomain.setPropertyIndices(indices, lowerReservedNames); @@ -663,16 +663,6 @@ private void checkCanUpdate(DatasetDefinition def, Container container, User use hasNameChange = !datasetPropertiesUpdate.getName().equals(oldDef.getName()); if (hasNameChange) changeDetails.append("The name of the dataset '" + oldDef.getName() + "' was changed to '" + datasetPropertiesUpdate.getName() + "'."); - changeDetails.append(DomainUtil.getStringPropChangeMsg("Description", oldDef.getDescription(), datasetPropertiesUpdate.getDescription())); - changeDetails.append(DomainUtil.getStringPropChangeMsg("Category", oldDef.getCategory(), datasetPropertiesUpdate.getCategory())); - changeDetails.append(DomainUtil.getStringPropChangeMsg("Label", oldDef.getLabel(), datasetPropertiesUpdate.getLabel())); - changeDetails.append(DomainUtil.getStringPropChangeMsg("KeyPropertyName", oldDef.getKeyPropertyName(), datasetPropertiesUpdate.getKeyPropertyName())); - changeDetails.append(DomainUtil.getStringPropChangeMsg("VisitDateColumnName", oldDef.getVisitDateColumnName(), datasetPropertiesUpdate.getVisitDatePropertyName())); - changeDetails.append(DomainUtil.getStringPropChangeMsg("Tag", oldDef.getTag(), datasetPropertiesUpdate.getTag())); - changeDetails.append(DomainUtil.getPropChangeMsg("KeyPropertyManaged", oldDef.getKeyManagementType() != Dataset.KeyManagementType.None, datasetPropertiesUpdate.isKeyPropertyManaged())); - changeDetails.append(DomainUtil.getPropChangeMsg("IsDemographicData", oldDef.isDemographicData(), datasetPropertiesUpdate.isDemographicData())); - changeDetails.append(DomainUtil.getPropChangeMsg("IsUseTimeKeyField", oldDef.getUseTimeKeyField(), datasetPropertiesUpdate.isUseTimeKeyField())); - changeDetails.append(DomainUtil.getPropChangeMsg("CohortId", oldDef.getCohortId(), datasetPropertiesUpdate.getCohortId())); } ValidationException exception = new ValidationException(); From 782f5abce70361d2c86db0547286e2a3a2ad529f Mon Sep 17 00:00:00 2001 From: XingY Date: Fri, 16 May 2025 15:33:18 -0700 Subject: [PATCH 16/30] clean --- .../labkey/api/exp/PropertyDescriptor.java | 22 +++++++++---------- .../api/property/DomainPropertyImpl.java | 16 +++++++------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/api/src/org/labkey/api/exp/PropertyDescriptor.java b/api/src/org/labkey/api/exp/PropertyDescriptor.java index 990b8e172ff..8cc4176fae6 100644 --- a/api/src/org/labkey/api/exp/PropertyDescriptor.java +++ b/api/src/org/labkey/api/exp/PropertyDescriptor.java @@ -543,23 +543,23 @@ public void setDatabaseDefaultValue(@Nullable Object databaseDefaultValue) public Map getAuditRecordMap(@Nullable String validatorStr, @Nullable String conditionalFormatStr) { Map map = new LinkedHashMap<>(); - if (getName() != null) + if (!StringUtils.isEmpty(getName())) map.put("Name", getName()); - if (getLabel() != null) + if (!StringUtils.isEmpty(getLabel())) map.put("Label", getLabel()); if (null != getPropertyType()) map.put("Type", getPropertyType().getXarName()); if (getPropertyType().getJdbcType().isText()) map.put("Scale", getScale()); - if (getDescription() != null) + if (!StringUtils.isEmpty(getDescription())) map.put("Description", getDescription()); - if (getFormat() != null) + if (!StringUtils.isEmpty(getFormat())) map.put("Format", getFormat()); - if (getURL() != null) + if (null !=getURL()) map.put("URL", getURL().toString()); - if (getPHI() != null) + if (null != getPHI()) map.put("PHI", getPHI().getLabel()); - if (getDefaultScale() != null) + if (null !=getDefaultScale()) map.put("DefaultScale", getDefaultScale().getLabel()); map.put("Required", isRequired()); map.put("Hidden", isHidden()); @@ -573,18 +573,18 @@ public Map getAuditRecordMap(@Nullable String validatorStr, @Nul map.put("RecommendedVariable", isRecommendedVariable()); map.put("ExcludedFromShifting", isExcludeFromShifting()); map.put("Scannable", isScannable()); - if (getDerivationDataScope() != null) + if (!StringUtils.isEmpty(getDerivationDataScope())) map.put("DerivationDataScope", getDerivationDataScope()); String importAliasStr = StringUtils.join(getImportAliasSet(), ","); if (!StringUtils.isEmpty(importAliasStr)) map.put("ImportAliases", importAliasStr); - if (getDefaultValueTypeEnum() != null) + if (null != getDefaultValueTypeEnum()) map.put("DefaultValueType", getDefaultValueTypeEnum().getLabel()); if (getLookup() != null) map.put("Lookup", getLookup().toJSONString()); - if (validatorStr != null) + if (!StringUtils.isEmpty(validatorStr)) map.put("Validator", validatorStr); - if (conditionalFormatStr != null) + if (!StringUtils.isEmpty(conditionalFormatStr)) map.put("ConditionalFormat", conditionalFormatStr); return map; diff --git a/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java index b8a9f16d2f0..fd2e345dc97 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java @@ -1150,19 +1150,19 @@ public String toString() public Map getAuditRecordMap(@Nullable String validatorStr, @Nullable String conditionalFormatStr) { Map map = new LinkedHashMap<>(); - if (getName() != null) + if (!StringUtils.isEmpty(getName())) map.put("Name", getName()); - if (getLabel() != null) + if (!StringUtils.isEmpty(getLabel())) map.put("Label", getLabel()); if (null != getPropertyType()) map.put("Type", getPropertyType().getXarName()); if (getPropertyType().getJdbcType().isText()) map.put("Scale", getScale()); - if (getDescription() != null) + if (!StringUtils.isEmpty(getDescription())) map.put("Description", getDescription()); - if (getFormat() != null) + if (!StringUtils.isEmpty(getFormat())) map.put("Format", getFormat()); - if (getURL() != null) + if (!StringUtils.isEmpty(getURL())) map.put("URL", getURL()); if (getPHI() != null) map.put("PHI", getPHI().getLabel()); @@ -1180,7 +1180,7 @@ public Map getAuditRecordMap(@Nullable String validatorStr, @Nul map.put("RecommendedVariable", isRecommendedVariable()); map.put("ExcludedFromShifting", isExcludeFromShifting()); map.put("Scannable", isScannable()); - if (getDerivationDataScope() != null) + if (!StringUtils.isEmpty(getDerivationDataScope())) map.put("DerivationDataScope", getDerivationDataScope()); String importAliasStr = StringUtils.join(getImportAliasSet(), ","); if (!StringUtils.isEmpty(importAliasStr)) @@ -1190,9 +1190,9 @@ public Map getAuditRecordMap(@Nullable String validatorStr, @Nul if (getLookup() != null) map.put("Lookup", getLookup().toJSONString()); - if (validatorStr != null) + if (!StringUtils.isEmpty(validatorStr)) map.put("Validator", validatorStr); - if (conditionalFormatStr != null) + if (!StringUtils.isEmpty(conditionalFormatStr)) map.put("ConditionalFormat", conditionalFormatStr); return map; From 8c721f25bcb58892ba60966712707a3f21991dfd Mon Sep 17 00:00:00 2001 From: XingY Date: Fri, 16 May 2025 15:49:37 -0700 Subject: [PATCH 17/30] clean --- .../labkey/api/exp/property/DomainUtil.java | 102 +++++------------- 1 file changed, 24 insertions(+), 78 deletions(-) diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index 833fe7c8b28..ddbe197ce5a 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -98,6 +98,7 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.regex.Matcher; @@ -752,59 +753,6 @@ public static ValidationException updateDomainDescriptor(GWTDomain " + " ;"; - if (oldProp == null) - return propertyName + ": -> " + newProp + ";"; - - if (oldProp.equals(newProp)) - return ""; - - return propertyName + ": " + oldProp + " -> " + newProp + ";"; - } - - public static String getPropChangeMsg(String propertyName, Object oldProp, Object newProp) - { - if (oldProp == newProp) - return ""; - - if (oldProp != null && newProp == null) - return getStringPropChangeMsg(propertyName, oldProp.toString(), null); - if (oldProp == null) - return getStringPropChangeMsg(propertyName, null, newProp.toString()); - - return getStringPropChangeMsg(propertyName, oldProp.toString(), newProp.toString()); - } - - public static String getCollectionPropChangeMsg(String propertyName, Collection oldProp, Collection newProp) - { - if (oldProp == newProp) - return ""; - - if (oldProp != null && newProp == null) - return getPropChangeMsg(propertyName, StringUtils.join(oldProp), null); - if (oldProp == null) - return getPropChangeMsg(propertyName, null, StringUtils.join(newProp)); - - String oldStr = oldProp - .stream() - .map(Object::toString) - .sorted().collect(Collectors.joining(", ")); - String newStr = newProp - .stream() - .map(Object::toString) - .sorted().collect(Collectors.joining(", ")); - return getPropChangeMsg(propertyName, oldStr, newStr); - } - /** @return Errors encountered during the save attempt */ @NotNull public static ValidationException updateDomainDescriptor(GWTDomain orig, GWTDomain update, Container container, User user, @@ -835,11 +783,16 @@ public static ValidationException updateDomainDescriptor(GWTDomain(); + if (oldProps == null) + oldProps = new LinkedHashMap<>(); + String updatedName = update.getName(); if (updateDomainName && !d.getName().equals(updatedName)) { - changeDetails.append(getStringPropChangeMsg("Name", d.getName(), updatedName)); + oldProps.put("Name", d.getName()); + newProps.put("Name", updatedName); updatedName = StringUtils.trimToEmpty(updatedName); String domainNameError = validateDomainName(updatedName, kind.getKindName(), kind.supportsNamingPattern()); if (!StringUtils.isEmpty(domainNameError)) @@ -851,25 +804,21 @@ public static ValidationException updateDomainDescriptor(GWTDomain oldDisabledSystemFields = d.getDisabledSystemFields(); + if (oldDisabledSystemFields != null && !oldDisabledSystemFields.isEmpty()) + oldProps.put("DisabledSystemFields", oldDisabledSystemFields); List disabledSystemFieldsUpdate = kind.getDisabledSystemFields(update.getDisabledSystemFields()); - changeDetails.append(getCollectionPropChangeMsg("DisabledSystemFields",d.getDisabledSystemFields(), disabledSystemFieldsUpdate)); + if (disabledSystemFieldsUpdate != null && !disabledSystemFieldsUpdate.isEmpty()) + newProps.put("DisabledSystemFields", disabledSystemFieldsUpdate); + d.setDisabledSystemFields(disabledSystemFieldsUpdate); Set baseIndices = kind.getPropertyIndices(d); List oldIndices = GWTIndex.toStringVals(orig.getIndices(), baseIndices); if (oldIndices != null && !oldIndices.isEmpty()) - { - if (oldProps == null) - oldProps = new LinkedHashMap<>(); - oldProps.put("Indices", oldIndices); - } + oldProps.put("Indices", oldIndices); List newIndices = GWTIndex.toStringVals(update.getIndices(), baseIndices); - if (newIndices != null && !newIndices.isEmpty()) - { - if (newProps == null) - newProps = new LinkedHashMap<>(); - newProps.put("Indices", newIndices); - } + newProps.put("Indices", newIndices); // NOTE that DomainImpl.save() does an optimistic concurrency check, but we still need to check here. // This code is diff'ing two GWTDomains and applying those changes to Domain d. We need to make sure we're @@ -990,9 +939,7 @@ public static ValidationException updateDomainDescriptor(GWTDomain(defaultValues); try @@ -1397,27 +1344,26 @@ private static void updateTextChoiceValueRows(Domain domain, User user, String p private static boolean _copyValidator(IPropertyValidator pv, GWTPropertyValidator gpv) { + boolean hasChange = false; if (pv != null && gpv != null) { - StringBuilder changeDetails = new StringBuilder(); - changeDetails.append(DomainUtil.getStringPropChangeMsg("Name", pv.getName(), gpv.getName())); + hasChange = !Objects.equals(pv.getName(), gpv.getName()); pv.setName(gpv.getName()); - changeDetails.append(DomainUtil.getStringPropChangeMsg("Description", pv.getDescription(), gpv.getDescription())); + hasChange = hasChange || !Objects.equals(pv.getDescription(), gpv.getDescription()); pv.setDescription(gpv.getDescription()); - changeDetails.append(DomainUtil.getStringPropChangeMsg("Expression", pv.getExpressionValue(), gpv.getExpression())); + hasChange = hasChange || !Objects.equals(pv.getExpressionValue(), gpv.getExpression()); pv.setExpressionValue(gpv.getExpression()); - changeDetails.append(DomainUtil.getStringPropChangeMsg("ErrorMsg", pv.getErrorMessage(), gpv.getErrorMessage())); + hasChange = hasChange || !Objects.equals(pv.getErrorMessage(), gpv.getErrorMessage()); pv.setErrorMessage(gpv.getErrorMessage()); - changeDetails.append(DomainUtil.getStringPropChangeMsg("Properties", PageFlowUtil.toQueryString(pv.getProperties().entrySet()), PageFlowUtil.toQueryString(gpv.getProperties().entrySet()))); + hasChange = hasChange || !Objects.equals(PageFlowUtil.toQueryString(pv.getProperties().entrySet()), PageFlowUtil.toQueryString(gpv.getProperties().entrySet())); for (Map.Entry entry : gpv.getProperties().entrySet()) { pv.setProperty(entry.getKey(), entry.getValue()); } - return !changeDetails.toString().isEmpty(); } - return false; + return hasChange; } private static String getDomainErrorMessage(@Nullable GWTDomain domain, String message) From 39237cea847c64a571619bf2a72002a667ec87da Mon Sep 17 00:00:00 2001 From: XingY Date: Fri, 16 May 2025 16:28:33 -0700 Subject: [PATCH 18/30] Merge branch 'develop' into fb_domainAudit # Conflicts: # biologics/package-lock.json # biologics/package.json # inventory/package-lock.json # inventory/package.json # sampleManagement/package-lock.json # sampleManagement/package.json --- core/package-lock.json | 8 ++++---- core/package.json | 2 +- experiment/package-lock.json | 8 ++++---- experiment/package.json | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/package-lock.json b/core/package-lock.json index f83733a37ce..4d02d816b51 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -8,7 +8,7 @@ "name": "labkey-core", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.41.0-fb-domainAudit.3", + "@labkey/components": "6.42.1-fb-domainAudit.1", "@labkey/themes": "1.4.2" }, "devDependencies": { @@ -3029,9 +3029,9 @@ } }, "node_modules/@labkey/components": { - "version": "6.41.0-fb-domainAudit.3", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.41.0-fb-domainAudit.3.tgz", - "integrity": "sha512-vEWzebLZhHUJlvp70vDVW894w8YLc+rA7CqmqVWtXV+gQLnh+bYsDFmwHJF6nrugMUFQs/Clk4sVIQ5YpOrLng==", + "version": "6.42.1-fb-domainAudit.1", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.42.1-fb-domainAudit.1.tgz", + "integrity": "sha512-3gy9+fFx3TQCo1Ak4tPYInMNln4sLVAB3hQLZL5rwr3epfa1QLUzVC0cDzeIsnpQwnC7yXmFWrhzwqRZqeBiEQ==", "dependencies": { "@hello-pangea/dnd": "18.0.1", "@labkey/api": "1.40.1-fb-domainAudit.2", diff --git a/core/package.json b/core/package.json index ba26773cab9..7be3a5c8c08 100644 --- a/core/package.json +++ b/core/package.json @@ -53,7 +53,7 @@ } }, "dependencies": { - "@labkey/components": "6.41.0-fb-domainAudit.3", + "@labkey/components": "6.42.1-fb-domainAudit.1", "@labkey/themes": "1.4.2" }, "devDependencies": { diff --git a/experiment/package-lock.json b/experiment/package-lock.json index 448d49fcf9b..21049a20861 100644 --- a/experiment/package-lock.json +++ b/experiment/package-lock.json @@ -8,7 +8,7 @@ "name": "experiment", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.41.0-fb-domainAudit.3" + "@labkey/components": "6.42.1-fb-domainAudit.1" }, "devDependencies": { "@labkey/build": "8.5.0", @@ -2842,9 +2842,9 @@ } }, "node_modules/@labkey/components": { - "version": "6.41.0-fb-domainAudit.3", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.41.0-fb-domainAudit.3.tgz", - "integrity": "sha512-vEWzebLZhHUJlvp70vDVW894w8YLc+rA7CqmqVWtXV+gQLnh+bYsDFmwHJF6nrugMUFQs/Clk4sVIQ5YpOrLng==", + "version": "6.42.1-fb-domainAudit.1", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.42.1-fb-domainAudit.1.tgz", + "integrity": "sha512-3gy9+fFx3TQCo1Ak4tPYInMNln4sLVAB3hQLZL5rwr3epfa1QLUzVC0cDzeIsnpQwnC7yXmFWrhzwqRZqeBiEQ==", "dependencies": { "@hello-pangea/dnd": "18.0.1", "@labkey/api": "1.40.1-fb-domainAudit.2", diff --git a/experiment/package.json b/experiment/package.json index cb48d3ef243..fbb9dac92ee 100644 --- a/experiment/package.json +++ b/experiment/package.json @@ -13,7 +13,7 @@ "test-integration": "cross-env NODE_ENV=test jest --ci --runInBand -c test/js/jest.config.integration.js" }, "dependencies": { - "@labkey/components": "6.41.0-fb-domainAudit.3" + "@labkey/components": "6.42.1-fb-domainAudit.1" }, "devDependencies": { "@labkey/build": "8.5.0", From 59d7f378a96f68461eb440e268e507f73454bd40 Mon Sep 17 00:00:00 2001 From: XingY Date: Mon, 19 May 2025 00:17:19 -0700 Subject: [PATCH 19/30] fixes and update tests --- .../org/labkey/api/exp/property/DomainUtil.java | 3 ++- .../experiment/api/SampleTypeServiceImpl.java | 15 +++++++++++---- .../experiment/api/property/DomainImpl.java | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index ddbe197ce5a..33c94bf5712 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -818,7 +818,8 @@ public static ValidationException updateDomainDescriptor(GWTDomain newIndices = GWTIndex.toStringVals(update.getIndices(), baseIndices); - newProps.put("Indices", newIndices); + if (newIndices != null && !newIndices.isEmpty()) + newProps.put("Indices", newIndices); // NOTE that DomainImpl.save() does an optimistic concurrency check, but we still need to check here. // This code is diff'ing two GWTDomains and applying those changes to Domain d. We need to make sure we're diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index f374af671c4..c3bb127135b 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -1007,8 +1007,8 @@ public ValidationException updateSampleType(GWTDomain oldProps = st.getAuditRecordMap(); - Map newProps = options != null ? options.getAuditRecordMap() : st.getAuditRecordMap() /* no update */; + Map oldProps = new LinkedHashMap<>(); + Map newProps = new LinkedHashMap<>(); String newName = StringUtils.trimToNull(update.getName()); String oldSampleTypeName = st.getName(); @@ -1026,11 +1026,18 @@ public ValidationException updateSampleType(GWTDomain oldProps_ = st.getAuditRecordMap(); + Map newProps_ = options != null ? options.getAuditRecordMap() : st.getAuditRecordMap() /* no update */; + newProps.putAll(newProps_); + oldProps.putAll(oldProps_); + boolean hasMetricUnitChanged = false; if (options != null) diff --git a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java index 6abc6689d76..29d2dd5cab1 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java @@ -1054,7 +1054,7 @@ private PropertyChangeAuditInfoDetail makeModifiedPropAuditComment(DomainPropert } String changeSummary = changed.isEmpty() ? null : - "The following " + (changed.size() > 1 ? "properties were " : "property was") + " updated: " + StringUtils.join(changed, ", "); + "The following " + (changed.size() > 1 ? "properties were" : "property was") + " updated: " + StringUtils.join(changed, ", "); return new PropertyChangeAuditInfoDetail(changeSummary, oldProps, newProps); } From 301f215ff2c486970210d5f8265e12de8cdfb8dc Mon Sep 17 00:00:00 2001 From: XingY Date: Mon, 19 May 2025 23:39:26 -0700 Subject: [PATCH 20/30] update tests --- .../test/tests/study/StudyDatasetsTest.java | 24 +++++++++++++------ .../labkey/test/tests/study/StudyTest.java | 16 ++++++++++--- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java b/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java index abcd0a3ac58..a920d15b3f4 100644 --- a/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java +++ b/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -254,11 +255,13 @@ protected void renameDataset(@Nullable String error, String orgName, String newN if (error == null) { editDatasetPage.clickSave(); - checker().verifyEquals("The comment logged for the list update was not as expected.", - "The name of the dataset '" + orgName + "' was changed to '" + newName.trim() + "'." + - "Label: " + orgLabel + " -> " + newLabel.trim() + "; The descriptor of domain " + orgName + " was updated", - _auditLogHelper.getLastDomainEventComment(getProjectName(), orgName)); - + String changeDetails = "Name: " + orgName + " > " + newName.trim(); + changeDetails += "\nLabel: " + orgLabel + " > " + newLabel.trim(); + AuditLogHelper.DetailedAuditEventRow expectedDomainEvent = new AuditLogHelper.DetailedAuditEventRow(null, orgName, null, + "The name of the dataset '" + orgName + "' was changed to '" + newName.trim() + "'.", + "", null, null, changeDetails); + boolean pass = _auditLogHelper.validateLastDomainAuditEvents(orgName, getProjectName(), expectedDomainEvent, Collections.emptyMap()); + checker().verifyTrue("The comment logged for the dataset renaming was not as expected", pass); } else { @@ -318,8 +321,15 @@ protected void deleteFields(String name) assertEquals(Arrays.asList("XTest"), remainingFields); editDatasetPage.clickSave(); - checker().verifyEquals("Domain audit comment not as expected after removing fields", "The column(s) of domain " + name + " were modified", _auditLogHelper.getLastDomainEventComment(getProjectName(), name)); - checker().verifyEqualsSorted("Domain field audit comment not as expected after removing fields", List.of("Deleted", "Deleted"), _auditLogHelper.getLastDomainPropertyValues(getProjectName(), name, "Action")); + + AuditLogHelper.DetailedAuditEventRow expectedDomainEvent = new AuditLogHelper.DetailedAuditEventRow(null, name, null, + "The column(s) of domain " + name + " were modified.", + "", null, null, null); + boolean pass = _auditLogHelper.validateLastDomainAuditEvents(name, getProjectName(), expectedDomainEvent, + Map.of("YTest", new AuditLogHelper.DetailedAuditEventRow(null, "YTest", "Deleted",null,null, null, null, null), + "ZTest", new AuditLogHelper.DetailedAuditEventRow(null, "ZTest", "Deleted",null,null, null, null, null) + )); + checker().verifyTrue("Domain audit log not as expected after removing fields", pass); } @LogMethod diff --git a/study/test/src/org/labkey/test/tests/study/StudyTest.java b/study/test/src/org/labkey/test/tests/study/StudyTest.java index b212b9f77ac..ae70ad9912d 100644 --- a/study/test/src/org/labkey/test/tests/study/StudyTest.java +++ b/study/test/src/org/labkey/test/tests/study/StudyTest.java @@ -43,6 +43,7 @@ import org.labkey.test.pages.study.ManageStudyPage; import org.labkey.test.params.FieldDefinition; import org.labkey.test.tests.StudyBaseTest; +import org.labkey.test.util.AuditLogHelper; import org.labkey.test.util.ChartHelper; import org.labkey.test.util.DataRegionExportHelper; import org.labkey.test.util.DataRegionTable; @@ -1135,7 +1136,12 @@ protected void verifyManageDatasetsPage() setDemographicsBit(DEMOGRAPHICS_TITLE, false) .clickViewData(); - checker().verifyEquals("Domain audit comment not as expected after changing demographic bit", "IsDemographicData: true -> false; The descriptor of domain DEM-1 was updated", _auditLogHelper.getLastDomainEventComment(getProjectName(), DEMOGRAPHICS_DOMAIN_NAME)); + String changeDetails = "IsDemographicData: true > false" ; + AuditLogHelper.DetailedAuditEventRow expectedDomainEvent = new AuditLogHelper.DetailedAuditEventRow(null, DEMOGRAPHICS_DOMAIN_NAME, null, + "The descriptor of domain DEM-1 was updated", + "", null, null, changeDetails); + boolean pass = _auditLogHelper.validateLastDomainAuditEvents(DEMOGRAPHICS_DOMAIN_NAME, getProjectName(), expectedDomainEvent, Collections.emptyMap()); + checker().verifyTrue("Domain audit comment not as expected after changing demographic bit", pass); log("verify "); _customizeViewsHelper.openCustomizeViewPanel(); @@ -1296,8 +1302,12 @@ protected void verifyParticipantVisitDay() .clickApply() .clickSave(); - checker().verifyEquals("Domain audit comment not as expected after changing visit date column", "VisitDateColumnName: -> DEMdt; The column(s) of domain DEM-1 were modified", _auditLogHelper.getLastDomainEventComment(getProjectName(), DEMOGRAPHICS_DOMAIN_NAME)); - checker().verifyEquals("Domain field audit comment not as expected", List.of("VisitDay"), _auditLogHelper.getLastDomainPropertyValues(getProjectName(), DEMOGRAPHICS_DOMAIN_NAME, "PropertyName")); + AuditLogHelper.DetailedAuditEventRow expectedDomainEvent = new AuditLogHelper.DetailedAuditEventRow(null, DEMOGRAPHICS_DOMAIN_NAME, null, + "The column(s) of domain DEM-1 were modified", + "", null, null, ""); + boolean pass = _auditLogHelper.validateLastDomainAuditEvents(DEMOGRAPHICS_DOMAIN_NAME, getProjectName(), expectedDomainEvent, + Map.of("VisitDay", new AuditLogHelper.DetailedAuditEventRow(null, "VisitDay", null, null, null, null, null, "VisitDateColumnName: > DEMdt"))); + checker().verifyTrue("Domain audit comment not as expected after changing visit date column", pass); new DatasetPropertiesPage(getDriver()) .clickViewData(); From b2bd762357f836c0c519d5b16e7cfc8a9533cabf Mon Sep 17 00:00:00 2001 From: XingY Date: Wed, 21 May 2025 00:10:44 -0700 Subject: [PATCH 21/30] support calculated field audit changes, genId update audit, update/add tests --- .../api/gwt/client/model/GWTDomain.java | 4 +- .../labkey/api/gwt/client/model/GWTIndex.java | 2 +- .../client/model/GWTPropertyDescriptor.java | 5 + .../labkey/api/exp/list/ListDefinition.java | 3 +- .../org/labkey/api/exp/property/Domain.java | 7 +- .../labkey/api/exp/property/DomainUtil.java | 6 +- .../labkey/api/query/MetadataColumnJSON.java | 48 ++++++ .../experiment/api/ExperimentServiceImpl.java | 4 +- .../experiment/api/SampleTypeServiceImpl.java | 4 +- .../experiment/api/property/DomainImpl.java | 142 +++++++++++++++++- .../controllers/exp/ExperimentController.java | 16 ++ .../labkey/list/model/ListDefinitionImpl.java | 7 +- .../org/labkey/list/model/ListDomainKind.java | 2 +- .../labkey/query/QueryServiceImplTestCase.jsp | 2 +- .../labkey/study/model/DatasetDomainKind.java | 2 +- .../test/tests/study/StudyDatasetsTest.java | 2 +- 16 files changed, 229 insertions(+), 27 deletions(-) diff --git a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTDomain.java b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTDomain.java index d802cdfc2c6..e0a8d400eb2 100644 --- a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTDomain.java +++ b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTDomain.java @@ -161,14 +161,14 @@ public List getFields(boolean includeCalculated) public List getFields() { if (standardFields == null) - standardFields = fields.stream().filter(f -> f.getValueExpression() == null).toList(); + standardFields = fields.stream().filter(f -> !f.isCalculatedColumn()).toList(); return standardFields; } public List getCalculatedFields() { if (calculatedFields == null) - calculatedFields = fields.stream().filter(f -> f.getValueExpression() != null).toList(); + calculatedFields = fields.stream().filter(GWTPropertyDescriptor::isCalculatedColumn).toList(); return calculatedFields; } diff --git a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java index a765bebf341..3edc2783fae 100644 --- a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java +++ b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTIndex.java @@ -89,7 +89,7 @@ public static List toStringVals(List indices, Set excludeIndices = excludeBaseIndices == null ? Collections.emptySet() : excludeBaseIndices.stream().map(PropertyStorageSpec.Index::toStringVal).collect(Collectors.toSet()); - return indices.stream().map(GWTIndex::toStringVal).filter(v -> !excludeIndices.contains(v)).toList(); + return indices.stream().map(GWTIndex::toStringVal).filter(v -> !excludeIndices.contains(v)).sorted().toList(); } } diff --git a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTPropertyDescriptor.java b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTPropertyDescriptor.java index d81c5af4cac..e0dea09382c 100644 --- a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTPropertyDescriptor.java +++ b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTPropertyDescriptor.java @@ -591,6 +591,11 @@ public String getValueExpression() return valueExpression.getString(); } + public boolean isCalculatedColumn() + { + return getValueExpression() != null; + } + public void setValueExpression(String valueExpression) { this.valueExpression.set(valueExpression); diff --git a/api/src/org/labkey/api/exp/list/ListDefinition.java b/api/src/org/labkey/api/exp/list/ListDefinition.java index 81843a110fe..796c60cc34a 100644 --- a/api/src/org/labkey/api/exp/list/ListDefinition.java +++ b/api/src/org/labkey/api/exp/list/ListDefinition.java @@ -25,6 +25,7 @@ import org.labkey.api.exp.DomainNotFoundException; import org.labkey.api.exp.PropertyType; import org.labkey.api.exp.property.Domain; +import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; import org.labkey.api.query.BatchValidationException; import org.labkey.api.query.QueryUpdateService; import org.labkey.api.reader.DataLoader; @@ -291,7 +292,7 @@ public static BodySetting getForValue(int value) void setKeyType(KeyType type); void save(User user) throws Exception; - void save(User user, boolean ensureKey, @Nullable Map newRecordMap) throws Exception; + void save(User user, boolean ensureKey, @Nullable Map newRecordMap, @Nullable List calculatedFields) throws Exception; void delete(User user) throws DomainNotFoundException; void delete(User user, @Nullable String auditUserComment) throws DomainNotFoundException; diff --git a/api/src/org/labkey/api/exp/property/Domain.java b/api/src/org/labkey/api/exp/property/Domain.java index 653b3c86b3b..4c0e4da5580 100644 --- a/api/src/org/labkey/api/exp/property/Domain.java +++ b/api/src/org/labkey/api/exp/property/Domain.java @@ -28,6 +28,7 @@ import org.labkey.api.exp.PropertyDescriptor; import org.labkey.api.exp.TemplateInfo; import org.labkey.api.gwt.client.model.GWTIndex; +import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; import org.labkey.api.security.User; import org.labkey.api.security.permissions.Permission; import org.labkey.api.view.ActionURL; @@ -88,8 +89,10 @@ default void delete(@Nullable User user, @Nullable String auditUserComment) thro delete(user); } void save(User user) throws ChangePropertyDescriptorException; - void save(User user, @Nullable Map newRecordMap) throws ChangePropertyDescriptorException; - void save(User user, @Nullable String auditComment, @Nullable String auditUserComment, @Nullable Map oldRecordMap, @Nullable Map newRecordMap) throws ChangePropertyDescriptorException; + void save(User user, @Nullable Map newRecordMap, @Nullable List calculatedFields) throws ChangePropertyDescriptorException; + void save(User user, @Nullable String auditComment, @Nullable String auditUserComment, + @Nullable Map oldRecordMap, @Nullable Map newRecordMap, + @Nullable List oldCalculatedFields, @Nullable List newCalculatedFields) throws ChangePropertyDescriptorException; /** Returns true if this domain has not yet been saved. */ boolean isNew(); diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index 33c94bf5712..dc741b10f89 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -940,7 +940,7 @@ public static ValidationException updateDomainDescriptor(GWTDomain(defaultValues); try @@ -1044,7 +1044,7 @@ private static void replaceLockedFields(Set lockedFields, { GWTPropertyDescriptor pd = updateFieldsIterator.next(); int propertyId = pd.getPropertyId(); - if (pd.getValueExpression() == null && origLockedFieldMap.containsKey(propertyId)) + if (!pd.isCalculatedColumn() && origLockedFieldMap.containsKey(propertyId)) { updateFieldsIterator.set(origLockedFieldMap.get(propertyId)); } @@ -1466,7 +1466,7 @@ public static ValidationException validateProperties(@Nullable Domain domain, @N } } - if (field.getValueExpression() != null && field.getValueExpression().trim().length() > 4000) + if (field.isCalculatedColumn() && field.getValueExpression().trim().length() > 4000) { exception.addFieldError(name, getDomainErrorMessage(updates, "The value expression for '" + name + "' is too long (" + field.getValueExpression().trim().length() + " characters). Please limit to 4000 characters.")); diff --git a/api/src/org/labkey/api/query/MetadataColumnJSON.java b/api/src/org/labkey/api/query/MetadataColumnJSON.java index b49aaa9a8a7..67b3080957c 100644 --- a/api/src/org/labkey/api/query/MetadataColumnJSON.java +++ b/api/src/org/labkey/api/query/MetadataColumnJSON.java @@ -16,8 +16,13 @@ package org.labkey.api.query; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; +import org.labkey.api.data.ConditionalFormat; import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; +import java.util.LinkedHashMap; +import java.util.Map; + @EqualsAndHashCode(callSuper = true) public class MetadataColumnJSON extends GWTPropertyDescriptor { @@ -115,4 +120,47 @@ public String getLookupDescription() return "(custom)"; return super.getLookupDescription(); } + + public Map getAuditRecordMap() + { + Map map = new LinkedHashMap<>(); + if (!StringUtils.isEmpty(getName())) + map.put("Name", getName()); + if (!StringUtils.isEmpty(getLabel())) + map.put("Label", getLabel()); + if (!StringUtils.isEmpty(getValueExpression())) + map.put("ValueExpression", getValueExpression()); + if (getScale() != null) + map.put("Scale", getScale()); + if (!StringUtils.isEmpty(getDescription())) + map.put("Description", getDescription()); + if (!StringUtils.isEmpty(getFormat())) + map.put("Format", getFormat()); + if (!StringUtils.isEmpty(getURL())) + map.put("URL", getURL()); + if (!StringUtils.isEmpty(getPHI())) + map.put("PHI", getPHI()); + if (!StringUtils.isEmpty(getDefaultScale())) + map.put("DefaultScale", getDefaultScale()); + map.put("Required", isRequired()); + map.put("Hidden", isHidden()); + map.put("Measure", isMeasure()); + map.put("Dimension", isDimension()); + map.put("ShownInInsert", isShownInInsertView()); + map.put("ShownInDetails", isShownInDetailsView()); + map.put("ShownInUpdate", isShownInUpdateView()); + map.put("RecommendedVariable", isRecommendedVariable()); + map.put("ExcludedFromShifting", isExcludeFromShifting()); + map.put("Scannable", isScannable()); + if (!StringUtils.isEmpty(getDerivationDataScope())) + map.put("DerivationDataScope", getDerivationDataScope()); + if (!StringUtils.isEmpty(getImportAliases())) + map.put("ImportAliases", getImportAliases()); + String conditionalFormatStr = ConditionalFormat.toStringVal(getConditionalFormats()); + if (!StringUtils.isEmpty(conditionalFormatStr)) + map.put("ConditionalFormat", conditionalFormatStr); + + return map; + } + } \ No newline at end of file diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index c00557cd07b..a21b90ae81b 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -7884,7 +7884,7 @@ public ExpDataClassImpl createDataClass( for (GWTPropertyDescriptor pd : properties) { // calculatedFields will be handled separately - if (pd.getValueExpression() != null) + if (pd.isCalculatedColumn()) { calculatedFields.add(pd); continue; @@ -7939,7 +7939,7 @@ else if (domain.getPropertyByName(propertyName) != null) // issue 25275 if (kind != null) domain.setPropertyForeignKeys(kind.getPropertyForeignKeys(c)); - domain.save(u, options == null ? null : options.getAuditRecordMap()); + domain.save(u, options == null ? null : options.getAuditRecordMap(), calculatedFields); impl.save(u); SchemaKey schemaKey = SchemaKey.fromParts(ExpSchema.SCHEMA_NAME, DataClassUserSchema.NAME); diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index c3bb127135b..9abda85fbd7 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -793,7 +793,7 @@ public ExpSampleTypeImpl createSampleType(Container c, User u, String name, Stri String propertyName = pd.getName().toLowerCase(); // calculatedFields will be handled separately - if (pd.getValueExpression() != null) + if (pd.isCalculatedColumn()) { calculatedFields.add(pd); continue; @@ -880,7 +880,7 @@ public ExpSampleTypeImpl createSampleType(Container c, User u, String name, Stri { try { - domain.save(u, changeDetails); + domain.save(u, changeDetails, calculatedFields); st.save(u); QueryService.get().saveCalculatedFieldsMetadata(SamplesSchema.SCHEMA_NAME, name, null, calculatedFields, false, u, c); DefaultValueService.get().setDefaultValues(domain.getContainer(), defaultValues); diff --git a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java index 29d2dd5cab1..2140455c0cf 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java @@ -59,8 +59,10 @@ import org.labkey.api.exp.property.DomainTemplate; import org.labkey.api.exp.property.DomainUtil; import org.labkey.api.gwt.client.model.GWTIndex; +import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; import org.labkey.api.query.BatchValidationException; import org.labkey.api.query.FieldKey; +import org.labkey.api.query.MetadataColumnJSON; import org.labkey.api.query.QueryService; import org.labkey.api.query.QueryUpdateService; import org.labkey.api.query.QueryUpdateServiceException; @@ -379,7 +381,7 @@ public boolean isNew() @Override public void save(User user) throws ChangePropertyDescriptorException { - save(user, null); + save(user, null, null); } @Override @@ -525,22 +527,26 @@ private void validatePropertyLookup(User user, DomainProperty dp) throws ChangeP public void saveIfNotExists(User user) throws ChangePropertyDescriptorException { - save(user, false, true, null, null, null, null); + save(user, false, true, null, null, null, null, null, null); } @Override - public void save(User user, @Nullable Map newRecordMap) throws ChangePropertyDescriptorException + public void save(User user, @Nullable Map newRecordMap, @Nullable List calculatedFields) throws ChangePropertyDescriptorException { - save(user, false, false, null, null, null, newRecordMap); + save(user, false, false, null, null, null, newRecordMap, null, calculatedFields); } @Override - public void save(User user, @Nullable String auditComment, @Nullable String auditUserComment, @Nullable Map oldRecordMap, @Nullable Map newRecordMap) throws ChangePropertyDescriptorException + public void save(User user, @Nullable String auditComment, @Nullable String auditUserComment, + @Nullable Map oldRecordMap, @Nullable Map newRecordMap, + @Nullable List oldCalculatedFields, @Nullable List newCalculatedFields) throws ChangePropertyDescriptorException { - save(user, false, false, auditComment, auditUserComment, oldRecordMap, newRecordMap); + save(user, false, false, auditComment, auditUserComment, oldRecordMap, newRecordMap, oldCalculatedFields, newCalculatedFields); } - public void save(User user, boolean allowAddBaseProperty, boolean saveOnlyIfNotExists, @Nullable String auditComment, @Nullable String auditUserComment, @Nullable Map oldRecordMap, @Nullable Map newRecordMap) throws ChangePropertyDescriptorException + public void save(User user, boolean allowAddBaseProperty, boolean saveOnlyIfNotExists, @Nullable String auditComment, @Nullable String auditUserComment, + @Nullable Map oldRecordMap, @Nullable Map newRecordMap, + @Nullable List oldCalculatedFields, @Nullable List newCalculatedFields) throws ChangePropertyDescriptorException { ExperimentService exp = ExperimentService.get(); @@ -814,6 +820,10 @@ else if (null != pdOld) } } + CalculatedFieldsUpdate calculatedFieldsUpdate = determineCalculatedFieldsUpdates(oldCalculatedFields, newCalculatedFields); + if (calculatedFieldsUpdate.hasChange()) + propChanged = true; + final boolean finalPropChanged = propChanged; final String extraAuditComment = StringUtils.isEmpty(auditComment) ? "" : auditComment + ' '; @@ -832,6 +842,7 @@ else if (null != pdOld) { final Long domainEventId = newDomainEventId != null ? newDomainEventId : addAuditEvent(user, extraAuditComment + columnModifiedMsg, auditUserComment, getContainer(), oldRecordMap, newRecordMap); propertyAuditInfo.forEach(auditInfo -> addPropertyAuditEvent(user, getContainer(), auditInfo.getProp(), auditInfo.getAction(), domainEventId, getName(), auditInfo.getDetails())); + calculatedFieldsUpdate.addAuditEvent(user, getContainer(), domainEventId, getName()); } else if (!isDomainNew) { @@ -855,6 +866,123 @@ else if (!isDomainNew) } } + record CalculatedFieldsUpdate(@Nullable List added, @Nullable List removed, @Nullable List> updated) + { + public boolean hasChange() + { + return (added != null && !added.isEmpty()) || (removed != null && !removed.isEmpty()) || (updated != null && !updated.isEmpty()); + } + + public void addAuditEvent(@Nullable User user, Container container, Long domainEventId, String domainName) + { + if (added != null) + { + for (MetadataColumnJSON field : added) + { + DomainPropertyAuditProvider.DomainPropertyAuditEvent event = + new DomainPropertyAuditProvider.DomainPropertyAuditEvent(container, null, field.getName(), + "Created", domainEventId, domainName, "Calculated field created."); + event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, field.getAuditRecordMap())); + AuditLogService.get().addEvent(user, event); + } + } + + if (removed != null) + { + for (MetadataColumnJSON field : removed) + { + DomainPropertyAuditProvider.DomainPropertyAuditEvent event = + new DomainPropertyAuditProvider.DomainPropertyAuditEvent(container, null, field.getName(), + "Deleted", domainEventId, domainName, "Calculated field removed."); + AuditLogService.get().addEvent(user, event); + } + } + + if (updated != null) + { + for (Pair oldNew : updated) + { + DomainPropertyAuditProvider.DomainPropertyAuditEvent event = + new DomainPropertyAuditProvider.DomainPropertyAuditEvent(container, null, oldNew.first.getName(), + "Modified", domainEventId, domainName, "Calculated field updated."); + event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, oldNew.first.getAuditRecordMap())); + event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, oldNew.second.getAuditRecordMap())); + AuditLogService.get().addEvent(user, event); + } + } + } + } + + private CalculatedFieldsUpdate determineCalculatedFieldsUpdates(@Nullable List oldCalculatedFields, @Nullable List newCalculatedFields) + { + Map oldFieldsMap = new HashMap<>(); + if (oldCalculatedFields != null) + oldCalculatedFields.forEach(field -> oldFieldsMap.put(field.getName(), field)); + Map newFieldsMap = new HashMap<>(); + if (newCalculatedFields != null) + newCalculatedFields.forEach(field -> newFieldsMap.put(field.getName(), field)); + + Set oldFields = oldFieldsMap.keySet(); + Set newFields = newFieldsMap.keySet(); + + List added = new ArrayList<>(newFields); + added.removeAll(oldFields); + List addedFields = new ArrayList<>(); + for (String field : added) + { + GWTPropertyDescriptor descriptor = newFieldsMap.get(field); + MetadataColumnJSON metadataColumnJSON = null; + if (descriptor instanceof MetadataColumnJSON) + metadataColumnJSON = (MetadataColumnJSON) descriptor; + else + metadataColumnJSON = new MetadataColumnJSON(descriptor); + addedFields.add(metadataColumnJSON); + } + + List deleted = new ArrayList<>(oldFields); + deleted.removeAll(newFields); + List removedFields = new ArrayList<>(); + for (String field : deleted) + { + GWTPropertyDescriptor descriptor = oldFieldsMap.get(field); + MetadataColumnJSON metadataColumnJSON = null; + if (descriptor instanceof MetadataColumnJSON) + metadataColumnJSON = (MetadataColumnJSON) descriptor; + else + metadataColumnJSON = new MetadataColumnJSON(descriptor); + removedFields.add(metadataColumnJSON); + } + + List retained = new ArrayList<>(oldFields); + retained.retainAll(newFields); + List> updatedFields = new ArrayList<>(); + for (String field : retained) + { + GWTPropertyDescriptor oldDescriptor = oldFieldsMap.get(field); + MetadataColumnJSON metadataColumnJSONOld = null; + if (oldDescriptor instanceof MetadataColumnJSON) + metadataColumnJSONOld = (MetadataColumnJSON) oldDescriptor; + else + metadataColumnJSONOld = new MetadataColumnJSON(oldDescriptor); + + GWTPropertyDescriptor newDescriptor = newFieldsMap.get(field); + MetadataColumnJSON metadataColumnJSONNew = null; + if (newDescriptor instanceof MetadataColumnJSON) + metadataColumnJSONNew = (MetadataColumnJSON) newDescriptor; + else + metadataColumnJSONNew = new MetadataColumnJSON(newDescriptor); + + Map oldDetails = metadataColumnJSONOld.getAuditRecordMap(); + Map newDetails = metadataColumnJSONNew.getAuditRecordMap(); + if (oldDetails.equals(newDetails)) + continue; + + updatedFields.add(new Pair<>(metadataColumnJSONOld, metadataColumnJSONNew)); + } + + return new CalculatedFieldsUpdate(addedFields, removedFields, updatedFields); + } + private void ensureUniqueIdValues(List propsAdded) throws SQLException, BatchValidationException { SchemaTableInfo table = StorageProvisioner.get().getSchemaTableInfo(this); diff --git a/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java b/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java index eb2217ed5d8..fc570799d85 100644 --- a/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java +++ b/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java @@ -132,6 +132,7 @@ import org.labkey.api.exp.api.SampleTypeService; import org.labkey.api.exp.list.ListService; import org.labkey.api.exp.property.Domain; +import org.labkey.api.exp.property.DomainAuditProvider; import org.labkey.api.exp.property.DomainKind; import org.labkey.api.exp.property.DomainProperty; import org.labkey.api.exp.property.DomainTemplate; @@ -8013,6 +8014,7 @@ public Object execute(EntitySequenceForm form, BindException errors) try { + Domain domain = null; if (SampleTypeDomainKind.NAME.equalsIgnoreCase(form.getKindName())) { if (form.getSeqType() == NameGenerator.EntityCounter.genId) @@ -8022,7 +8024,10 @@ public Object execute(EntitySequenceForm form, BindException errors) ExpSampleType sampleType = SampleTypeService.get().getSampleType(form.getRowId()); if (sampleType != null) + { sampleType.ensureMinGenId(form.getNewValue()); + domain = sampleType.getDomain(); + } else { resp.put("success", false); @@ -8044,13 +8049,24 @@ else if (DataClassDomainKind.NAME.equalsIgnoreCase(form.getKindName())) ExpDataClass dataClass = ExperimentService.get().getDataClass(form.getRowId()); if (dataClass != null) + { dataClass.ensureMinGenId(form.getNewValue(), getContainer()); + domain = dataClass.getDomain(); + } else { resp.put("success", false); resp.put("error", "DataClass does not exist."); } } + + if (domain != null) + { + DomainAuditProvider.DomainAuditEvent event = new DomainAuditProvider.DomainAuditEvent(getContainer(), "The genId for domain " + domain.getName() + " has been updated to " + form.getNewValue() + "."); + event.setDomainUri(domain.getTypeURI()); + event.setDomainName(domain.getName()); + AuditLogService.get().addEvent(getUser(), event); + } } catch (ExperimentException e) { diff --git a/list/src/org/labkey/list/model/ListDefinitionImpl.java b/list/src/org/labkey/list/model/ListDefinitionImpl.java index ef6155d1116..5cdf7a24821 100644 --- a/list/src/org/labkey/list/model/ListDefinitionImpl.java +++ b/list/src/org/labkey/list/model/ListDefinitionImpl.java @@ -40,6 +40,7 @@ import org.labkey.api.exp.property.Domain; import org.labkey.api.exp.property.DomainProperty; import org.labkey.api.exp.property.PropertyService; +import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; import org.labkey.api.query.BatchValidationException; import org.labkey.api.query.FieldKey; import org.labkey.api.query.QueryAction; @@ -370,13 +371,13 @@ public void setFileAttachmentIndex(boolean fileAttachmentIndex) @Override public void save(User user) throws Exception { - save(user, true, null); + save(user, true, null, null); } private static final ReentrantLock _saveLock = new ReentrantLockWithName(ListDefinitionImpl.class, "_saveLock"); @Override - public void save(User user, boolean ensureKey, @Nullable Map newRecordMap) throws Exception + public void save(User user, boolean ensureKey, @Nullable Map newRecordMap, @Nullable List calculatedFields) throws Exception { if (ensureKey) { @@ -396,7 +397,7 @@ public void save(User user, boolean ensureKey, @Nullable Map new // The domain kind cannot lookup the list definition if the domain has not been saved ((ListDomainKind) domain.getDomainKind()).setListDefinition(this); - domain.save(user, newRecordMap); + domain.save(user, newRecordMap, calculatedFields); _def.setDomainId(domain.getTypeId()); ListDef inserted = ListManager.get().insert(user, _def, _preferredListIds); diff --git a/list/src/org/labkey/list/model/ListDomainKind.java b/list/src/org/labkey/list/model/ListDomainKind.java index e9906e520e7..55e67e2ec2d 100644 --- a/list/src/org/labkey/list/model/ListDomainKind.java +++ b/list/src/org/labkey/list/model/ListDomainKind.java @@ -436,7 +436,7 @@ public Domain createDomain(GWTDomain domain, ListDomainKindProperties listProper // TODO: This looks like the wrong order to me -- we should updateListProperties() (persist the indexing // settings and handle potential transitions) before calling save() (which indexes the list). Since this is // the create case there's no data to index, but there is meta data... - list.save(user, true, listProperties.getAuditRecordMap()); + list.save(user, true, listProperties.getAuditRecordMap(), domain.getCalculatedFields()); updateListProperties(container, user, list.getListId(), listProperties); QueryService.get().saveCalculatedFieldsMetadata(ListQuerySchema.NAME, name, null, domain.getCalculatedFields(), false, user, container); diff --git a/query/src/org/labkey/query/QueryServiceImplTestCase.jsp b/query/src/org/labkey/query/QueryServiceImplTestCase.jsp index d93b1a2a223..352e4097c4d 100644 --- a/query/src/org/labkey/query/QueryServiceImplTestCase.jsp +++ b/query/src/org/labkey/query/QueryServiceImplTestCase.jsp @@ -142,7 +142,7 @@ d.addProperty(new PropertyStorageSpec("A", JdbcType.INTEGER)); d.addProperty(new PropertyStorageSpec("B", JdbcType.INTEGER)); d.addProperty(new PropertyStorageSpec("C", JdbcType.INTEGER)); - list.save(user,true, null); + list.save(user,true, null, null); } diff --git a/study/src/org/labkey/study/model/DatasetDomainKind.java b/study/src/org/labkey/study/model/DatasetDomainKind.java index 4cadd7b911d..ba213b65f06 100644 --- a/study/src/org/labkey/study/model/DatasetDomainKind.java +++ b/study/src/org/labkey/study/model/DatasetDomainKind.java @@ -514,7 +514,7 @@ else if (!timepointType.isVisitBased() && getKindName().equals(VisitDatasetDomai DomainUtil.addProperty(newDomain, pd, defaultValues, propertyUris, null); } - newDomain.save(user, arguments.getAuditRecordMap()); + newDomain.save(user, arguments.getAuditRecordMap(), domain.getCalculatedFields()); List indices = (List)domain.getIndices(); newDomain.setPropertyIndices(indices, lowerReservedNames); diff --git a/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java b/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java index a920d15b3f4..3d8f6b3f6a0 100644 --- a/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java +++ b/study/test/src/org/labkey/test/tests/study/StudyDatasetsTest.java @@ -258,7 +258,7 @@ protected void renameDataset(@Nullable String error, String orgName, String newN String changeDetails = "Name: " + orgName + " > " + newName.trim(); changeDetails += "\nLabel: " + orgLabel + " > " + newLabel.trim(); AuditLogHelper.DetailedAuditEventRow expectedDomainEvent = new AuditLogHelper.DetailedAuditEventRow(null, orgName, null, - "The name of the dataset '" + orgName + "' was changed to '" + newName.trim() + "'.", + "The name of the dataset '" + orgName + "' was changed to '" + newName.trim() + "'. The descriptor of domain " + orgName + " was updated.", "", null, null, changeDetails); boolean pass = _auditLogHelper.validateLastDomainAuditEvents(orgName, getProjectName(), expectedDomainEvent, Collections.emptyMap()); checker().verifyTrue("The comment logged for the dataset renaming was not as expected", pass); From 606bdd7d68feddb33a9c272993e9c23aa5374fbe Mon Sep 17 00:00:00 2001 From: XingY Date: Wed, 21 May 2025 09:28:47 -0700 Subject: [PATCH 22/30] revert isCalculatedColumn getter --- api/gwtsrc/org/labkey/api/gwt/client/model/GWTDomain.java | 4 ++-- .../labkey/api/gwt/client/model/GWTPropertyDescriptor.java | 5 ----- api/src/org/labkey/api/exp/property/DomainUtil.java | 4 ++-- .../src/org/labkey/experiment/api/ExperimentServiceImpl.java | 2 +- .../src/org/labkey/experiment/api/SampleTypeServiceImpl.java | 2 +- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTDomain.java b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTDomain.java index e0a8d400eb2..d802cdfc2c6 100644 --- a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTDomain.java +++ b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTDomain.java @@ -161,14 +161,14 @@ public List getFields(boolean includeCalculated) public List getFields() { if (standardFields == null) - standardFields = fields.stream().filter(f -> !f.isCalculatedColumn()).toList(); + standardFields = fields.stream().filter(f -> f.getValueExpression() == null).toList(); return standardFields; } public List getCalculatedFields() { if (calculatedFields == null) - calculatedFields = fields.stream().filter(GWTPropertyDescriptor::isCalculatedColumn).toList(); + calculatedFields = fields.stream().filter(f -> f.getValueExpression() != null).toList(); return calculatedFields; } diff --git a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTPropertyDescriptor.java b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTPropertyDescriptor.java index e0dea09382c..d81c5af4cac 100644 --- a/api/gwtsrc/org/labkey/api/gwt/client/model/GWTPropertyDescriptor.java +++ b/api/gwtsrc/org/labkey/api/gwt/client/model/GWTPropertyDescriptor.java @@ -591,11 +591,6 @@ public String getValueExpression() return valueExpression.getString(); } - public boolean isCalculatedColumn() - { - return getValueExpression() != null; - } - public void setValueExpression(String valueExpression) { this.valueExpression.set(valueExpression); diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index dc741b10f89..74dc6d8b26d 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -1044,7 +1044,7 @@ private static void replaceLockedFields(Set lockedFields, { GWTPropertyDescriptor pd = updateFieldsIterator.next(); int propertyId = pd.getPropertyId(); - if (!pd.isCalculatedColumn() && origLockedFieldMap.containsKey(propertyId)) + if (pd.getValueExpression() == null && origLockedFieldMap.containsKey(propertyId)) { updateFieldsIterator.set(origLockedFieldMap.get(propertyId)); } @@ -1466,7 +1466,7 @@ public static ValidationException validateProperties(@Nullable Domain domain, @N } } - if (field.isCalculatedColumn() && field.getValueExpression().trim().length() > 4000) + if (field.getValueExpression() != null && field.getValueExpression().trim().length() > 4000) { exception.addFieldError(name, getDomainErrorMessage(updates, "The value expression for '" + name + "' is too long (" + field.getValueExpression().trim().length() + " characters). Please limit to 4000 characters.")); diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index a21b90ae81b..7418e2f9c3e 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -7884,7 +7884,7 @@ public ExpDataClassImpl createDataClass( for (GWTPropertyDescriptor pd : properties) { // calculatedFields will be handled separately - if (pd.isCalculatedColumn()) + if (pd.getValueExpression() != null) { calculatedFields.add(pd); continue; diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index 9abda85fbd7..997558a7d59 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -793,7 +793,7 @@ public ExpSampleTypeImpl createSampleType(Container c, User u, String name, Stri String propertyName = pd.getName().toLowerCase(); // calculatedFields will be handled separately - if (pd.isCalculatedColumn()) + if (pd.getValueExpression() != null) { calculatedFields.add(pd); continue; From a5b72b650fcdba08307e26aed0def760e2624e9a Mon Sep 17 00:00:00 2001 From: XingY Date: Wed, 21 May 2025 09:55:01 -0700 Subject: [PATCH 23/30] Fix text -> flag field type conversion audit --- api/src/org/labkey/api/exp/PropertyDescriptor.java | 7 ++++++- .../labkey/experiment/api/property/DomainPropertyImpl.java | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/api/src/org/labkey/api/exp/PropertyDescriptor.java b/api/src/org/labkey/api/exp/PropertyDescriptor.java index 229aeea6340..f607e384250 100644 --- a/api/src/org/labkey/api/exp/PropertyDescriptor.java +++ b/api/src/org/labkey/api/exp/PropertyDescriptor.java @@ -544,7 +544,12 @@ public Map getAuditRecordMap(@Nullable String validatorStr, @Nul if (!StringUtils.isEmpty(getLabel())) map.put("Label", getLabel()); if (null != getPropertyType()) - map.put("Type", getPropertyType().getXarName()); + { + if (org.labkey.api.gwt.client.ui.PropertyType.expFlag.getURI().equals(getConceptURI())) + map.put("Type", "Flag"); + else + map.put("Type", getPropertyType().getXarName()); + } if (getPropertyType().getJdbcType().isText()) map.put("Scale", getScale()); if (!StringUtils.isEmpty(getDescription())) diff --git a/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java index fd2e345dc97..51bafa688eb 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainPropertyImpl.java @@ -1155,7 +1155,12 @@ public Map getAuditRecordMap(@Nullable String validatorStr, @Nul if (!StringUtils.isEmpty(getLabel())) map.put("Label", getLabel()); if (null != getPropertyType()) - map.put("Type", getPropertyType().getXarName()); + { + if (org.labkey.api.gwt.client.ui.PropertyType.expFlag.getURI().equals(getConceptURI())) + map.put("Type", "Flag"); + else + map.put("Type", getPropertyType().getXarName()); + } if (getPropertyType().getJdbcType().isText()) map.put("Scale", getScale()); if (!StringUtils.isEmpty(getDescription())) From ec5aef7edb169118a1f6d0be780edf80bf437533 Mon Sep 17 00:00:00 2001 From: XingY Date: Wed, 21 May 2025 23:23:16 -0700 Subject: [PATCH 24/30] add more tests --- .../org/labkey/api/exp/api/DataClassDomainKindProperties.java | 2 +- .../org/labkey/api/exp/api/SampleTypeDomainKindProperties.java | 2 +- experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/org/labkey/api/exp/api/DataClassDomainKindProperties.java b/api/src/org/labkey/api/exp/api/DataClassDomainKindProperties.java index d0d43eb2828..6fd087bf24d 100644 --- a/api/src/org/labkey/api/exp/api/DataClassDomainKindProperties.java +++ b/api/src/org/labkey/api/exp/api/DataClassDomainKindProperties.java @@ -209,7 +209,7 @@ public Map getAuditRecordMap() Map map = new LinkedHashMap<>(); // skip Name and Description since it's general domain property if (!StringUtils.isEmpty(getNameExpression())) - map.put("nameExpression", getNameExpression()); + map.put("NameExpression", getNameExpression()); String importAliasStr = ExperimentJSONConverter.getImportAliasStringVal(getImportAliases()); if (!StringUtils.isEmpty(importAliasStr)) map.put("ImportAlias", importAliasStr); diff --git a/api/src/org/labkey/api/exp/api/SampleTypeDomainKindProperties.java b/api/src/org/labkey/api/exp/api/SampleTypeDomainKindProperties.java index 0aa8f370d3c..f99ba383926 100644 --- a/api/src/org/labkey/api/exp/api/SampleTypeDomainKindProperties.java +++ b/api/src/org/labkey/api/exp/api/SampleTypeDomainKindProperties.java @@ -280,7 +280,7 @@ public Map getAuditRecordMap() Map map = new LinkedHashMap<>(); // skip Name and Description since it's general domain property if (!StringUtils.isEmpty(getNameExpression())) - map.put("nameExpression", getNameExpression()); + map.put("NameExpression", getNameExpression()); if (!StringUtils.isEmpty(getAliquotNameExpression())) map.put("AliquotNameExpression", getAliquotNameExpression()); if (!StringUtils.isEmpty(getLabelColor())) diff --git a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java index 5d053defa4f..7d232c19189 100644 --- a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java @@ -1120,7 +1120,7 @@ public Map getAuditRecordMap() if (!StringUtils.isEmpty(getName())) map.put("Name", getName()); if (!StringUtils.isEmpty(getNameExpression())) - map.put("nameExpression", getNameExpression()); + map.put("NameExpression", getNameExpression()); if (!StringUtils.isEmpty(getAliquotNameExpression())) map.put("AliquotNameExpression", getAliquotNameExpression()); if (!StringUtils.isEmpty(getLabelColor())) From 423c00f6dafd6e1bbe3d55d0c9f0f5f62dadfd97 Mon Sep 17 00:00:00 2001 From: XingY Date: Thu, 22 May 2025 13:49:11 -0700 Subject: [PATCH 25/30] fix more tests --- .../src/org/labkey/experiment/api/ExperimentServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index 7418e2f9c3e..091909b6b25 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -8910,7 +8910,7 @@ public Pair, Collection> ensureDataTypeContainerExclu } } - return new Pair<>(previousExclusions, updatedExclusions); + return new Pair<>(new TreeSet<>(previousExclusions), new TreeSet<>(updatedExclusions)); } @Override From 1ffc59df7e26ef0d246bdb1e01fb0abc38b7296f Mon Sep 17 00:00:00 2001 From: XingY Date: Thu, 22 May 2025 13:52:55 -0700 Subject: [PATCH 26/30] Issue 53108: Max length of Aliquot Naming Pattern is less than Naming Pattern and can be hit quickly with unicode characters --- api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java b/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java index 8fdef0f069e..4eab9c0a44b 100644 --- a/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java +++ b/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java @@ -483,7 +483,7 @@ public void validateOptions(Container container, User user, SampleTypeDomainKind int aliquotNameExpMax = materialSourceTI.getColumn("AliquotNameExpression").getScale(); if (StringUtils.isNotBlank(options.getAliquotNameExpression()) && options.getAliquotNameExpression().length() > aliquotNameExpMax) - throw new IllegalArgumentException("Value for Aliquot Naming Patten field may not exceed " + nameExpMax + " characters."); + throw new IllegalArgumentException("Value for Aliquot Naming Patten field may not exceed " + aliquotNameExpMax + " characters."); int labelColorMax = materialSourceTI.getColumn("LabelColor").getScale(); if (StringUtils.isNotBlank(options.getLabelColor()) && options.getLabelColor().length() > labelColorMax) From 92ac24f0e9ef3c3546399fb965f8193341e55872 Mon Sep 17 00:00:00 2001 From: XingY Date: Thu, 22 May 2025 14:10:09 -0700 Subject: [PATCH 27/30] code review changes --- .../api/audit/AbstractAuditTypeProvider.java | 23 +++++++++++++++++++ .../org/labkey/api/exp/api/ExpProtocol.java | 2 +- .../api/exp/property/DomainAuditProvider.java | 6 +---- .../property/DomainPropertyAuditProvider.java | 6 +---- .../labkey/assay/AssayDomainServiceImpl.java | 2 +- .../experiment/api/ExpProtocolImpl.java | 2 +- .../samples/SampleTimelineAuditProvider.java | 5 +--- .../labkey/list/model/ListAuditProvider.java | 2 +- .../query/audit/QueryUpdateAuditProvider.java | 6 +---- .../audit/ParticipantGroupAuditProvider.java | 2 +- .../study/dataset/DatasetAuditProvider.java | 2 +- 11 files changed, 33 insertions(+), 25 deletions(-) diff --git a/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java b/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java index 189cd73754f..2116b841e95 100644 --- a/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java +++ b/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java @@ -15,6 +15,7 @@ */ package org.labkey.api.audit; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.audit.data.DataMapColumn; @@ -49,6 +50,7 @@ import org.labkey.api.exp.property.PropertyService; import org.labkey.api.gwt.client.DefaultValueType; import org.labkey.api.query.AliasedColumn; +import org.labkey.api.query.DetailsURL; import org.labkey.api.query.FieldKey; import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; @@ -346,6 +348,16 @@ public static Container getDomainContainer() } protected void appendValueMapColumns(AbstractTableInfo table) + { + appendValueMapColumns(table, null); + } + + protected void appendValueMapColumns(AbstractTableInfo table, String eventName) + { + appendValueMapColumns(table, eventName, false); + } + + protected void appendValueMapColumns(AbstractTableInfo table, String eventName, boolean noUrl) { MutableColumnInfo oldCol = table.getMutableColumn(FieldKey.fromString(OLD_RECORD_PROP_NAME)); MutableColumnInfo newCol = table.getMutableColumn(FieldKey.fromString(NEW_RECORD_PROP_NAME)); @@ -371,6 +383,17 @@ protected void appendValueMapColumns(AbstractTableInfo table) // add a column to show the differences between old and new values if (oldCol != null && newCol != null) table.addColumn(new DataMapDiffColumn(table, COLUMN_NAME_DATA_CHANGES, oldCol, newCol)); + + if (!noUrl) + { + String urlStr = "audit-detailedAuditChanges.view?auditRowId=${rowId}"; + if (StringUtils.isEmpty(eventName)) + urlStr = urlStr + "&auditEventType=" + eventName; + DetailsURL url = DetailsURL.fromString(urlStr); + url.setStrictContainerContextEval(true); + table.setDetailsURL(url); + } + } @Override diff --git a/api/src/org/labkey/api/exp/api/ExpProtocol.java b/api/src/org/labkey/api/exp/api/ExpProtocol.java index ae60d93bede..5754e4062d9 100644 --- a/api/src/org/labkey/api/exp/api/ExpProtocol.java +++ b/api/src/org/labkey/api/exp/api/ExpProtocol.java @@ -180,5 +180,5 @@ static boolean isSampleWorkflowProtocol(String lsid) return isSampleWorkflowTaskProtocol(lsid) || isSampleWorkflowJobProtocol(lsid); } - Map getAuditRecordMap(AssayProvider provider, Container container); + Map getAuditRecordMap(AssayProvider provider); } diff --git a/api/src/org/labkey/api/exp/property/DomainAuditProvider.java b/api/src/org/labkey/api/exp/property/DomainAuditProvider.java index ca4a00ae4e8..616c21bf64b 100644 --- a/api/src/org/labkey/api/exp/property/DomainAuditProvider.java +++ b/api/src/org/labkey/api/exp/property/DomainAuditProvider.java @@ -115,11 +115,7 @@ else if (COLUMN_NAME_USER_COMMENT.equalsIgnoreCase(col.getName())) } }; - appendValueMapColumns(table); - - DetailsURL url = DetailsURL.fromString("audit-detailedAuditChanges.view?auditRowId=${rowId}&auditEventType=" + EVENT_TYPE); - url.setStrictContainerContextEval(true); - table.setDetailsURL(url); + appendValueMapColumns(table, EVENT_TYPE); return table; } diff --git a/api/src/org/labkey/api/exp/property/DomainPropertyAuditProvider.java b/api/src/org/labkey/api/exp/property/DomainPropertyAuditProvider.java index b8b732e53bc..35d679d6782 100644 --- a/api/src/org/labkey/api/exp/property/DomainPropertyAuditProvider.java +++ b/api/src/org/labkey/api/exp/property/DomainPropertyAuditProvider.java @@ -139,11 +139,7 @@ public TableInfo getLookupTableInfo() } }; - appendValueMapColumns(table); - - DetailsURL url = DetailsURL.fromString("audit-detailedAuditChanges.view?auditRowId=${rowId}&auditEventType=" + EVENT_NAME); - url.setStrictContainerContextEval(true); - table.setDetailsURL(url); + appendValueMapColumns(table, EVENT_NAME); return table; } diff --git a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java index 69aa53f1ca0..64eb534d760 100644 --- a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java +++ b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java @@ -469,7 +469,7 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr if (protocol == null) throw new ValidationException("Assay design has been deleted"); - oldProps = protocol.getAuditRecordMap(AssayService.get().getProvider(protocol), getContainer()); + oldProps = protocol.getAuditRecordMap(AssayService.get().getProvider(protocol)); // ensure that the user has edit perms in this container if (!canUpdateProtocols()) diff --git a/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java b/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java index 4585201aa0d..a4747bbbf93 100644 --- a/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java @@ -482,7 +482,7 @@ public void setStatus(Status status) } @Override - public Map getAuditRecordMap(AssayProvider provider, Container container) + public Map getAuditRecordMap(AssayProvider provider) { Map map = new LinkedHashMap<>(); map.put("Name", getName()); diff --git a/experiment/src/org/labkey/experiment/samples/SampleTimelineAuditProvider.java b/experiment/src/org/labkey/experiment/samples/SampleTimelineAuditProvider.java index 31cb6e68062..603997fb8d9 100644 --- a/experiment/src/org/labkey/experiment/samples/SampleTimelineAuditProvider.java +++ b/experiment/src/org/labkey/experiment/samples/SampleTimelineAuditProvider.java @@ -129,11 +129,8 @@ else if (COLUMN_NAME_USER_COMMENT.equalsIgnoreCase(col.getName())) } }; table.setTitleColumn(SAMPLE_NAME_COLUMN_NAME); - appendValueMapColumns(table); + appendValueMapColumns(table, SampleTimelineAuditEvent.EVENT_TYPE); - DetailsURL url = DetailsURL.fromString("audit-detailedAuditChanges.view?auditRowId=${rowId}&auditEventType=" + SampleTimelineAuditEvent.EVENT_TYPE); - url.setStrictContainerContextEval(true); - table.setDetailsURL(url); return table; } diff --git a/list/src/org/labkey/list/model/ListAuditProvider.java b/list/src/org/labkey/list/model/ListAuditProvider.java index 7187cdb8b29..9ce0b13e22a 100644 --- a/list/src/org/labkey/list/model/ListAuditProvider.java +++ b/list/src/org/labkey/list/model/ListAuditProvider.java @@ -108,7 +108,7 @@ protected void initColumn(MutableColumnInfo col) } } }; - appendValueMapColumns(table); + appendValueMapColumns(table, null, true); // Render a details URL only for rows that have a listItemEntityId DetailsURL url = DetailsURL.fromString("list/listItemDetails.view?listId=${listId}&entityId=${listItemEntityId}&rowId=${rowId}", null, StringExpressionFactory.AbstractStringExpression.NullValueBehavior.NullResult); diff --git a/query/src/org/labkey/query/audit/QueryUpdateAuditProvider.java b/query/src/org/labkey/query/audit/QueryUpdateAuditProvider.java index 93e26112da3..9adc5421140 100644 --- a/query/src/org/labkey/query/audit/QueryUpdateAuditProvider.java +++ b/query/src/org/labkey/query/audit/QueryUpdateAuditProvider.java @@ -140,11 +140,7 @@ else if (COLUMN_NAME_USER_COMMENT.equalsIgnoreCase(col.getName())) } } }; - appendValueMapColumns(table); - - DetailsURL url = DetailsURL.fromString("audit-detailedAuditChanges.view?auditRowId=${rowId}&auditEventType=" + QUERY_UPDATE_AUDIT_EVENT); - url.setStrictContainerContextEval(true); - table.setDetailsURL(url); + appendValueMapColumns(table, QUERY_UPDATE_AUDIT_EVENT); return table; } diff --git a/study/src/org/labkey/study/audit/ParticipantGroupAuditProvider.java b/study/src/org/labkey/study/audit/ParticipantGroupAuditProvider.java index 66bf4ff6083..94d903e914f 100644 --- a/study/src/org/labkey/study/audit/ParticipantGroupAuditProvider.java +++ b/study/src/org/labkey/study/audit/ParticipantGroupAuditProvider.java @@ -105,7 +105,7 @@ else if (PARTICIPANT_GROUP_ID_COLUMN_NAME.equalsIgnoreCase(col.getName())) } } }; - appendValueMapColumns(table); + appendValueMapColumns(table, null, true); return table; } diff --git a/study/src/org/labkey/study/dataset/DatasetAuditProvider.java b/study/src/org/labkey/study/dataset/DatasetAuditProvider.java index 5d70107450d..9865ffd556c 100644 --- a/study/src/org/labkey/study/dataset/DatasetAuditProvider.java +++ b/study/src/org/labkey/study/dataset/DatasetAuditProvider.java @@ -122,7 +122,7 @@ public TableInfo getLookupTableInfo() } } }; - appendValueMapColumns(table); + appendValueMapColumns(table, null, true); DetailsURL url = DetailsURL.fromString("dataset/datasetAuditHistory.view?auditRowId=${rowId}"); url.setStrictContainerContextEval(true); From c26f2703938259b27a8109399923626c8b23f501 Mon Sep 17 00:00:00 2001 From: XingY Date: Thu, 22 May 2025 19:59:51 -0700 Subject: [PATCH 28/30] refactor --- .../org/labkey/api/audit/AbstractAuditHandler.java | 10 +++++----- .../api/audit/AbstractAuditTypeProvider.java | 2 +- .../labkey/api/audit/SampleTimelineAuditEvent.java | 4 ++-- .../experiment/api/SampleTypeServiceImpl.java | 8 ++++---- .../labkey/experiment/api/property/DomainImpl.java | 14 +++++++------- list/src/org/labkey/list/model/ListManager.java | 2 +- .../labkey/study/assay/StudyPublishManager.java | 4 ++-- .../study/audit/ParticipantGroupAuditProvider.java | 12 ++++++------ .../org/labkey/study/model/DatasetDefinition.java | 10 +++++----- study/src/org/labkey/study/model/StudyManager.java | 4 ++-- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/api/src/org/labkey/api/audit/AbstractAuditHandler.java b/api/src/org/labkey/api/audit/AbstractAuditHandler.java index a6491624442..f21ff0ca9a0 100644 --- a/api/src/org/labkey/api/audit/AbstractAuditHandler.java +++ b/api/src/org/labkey/api/audit/AbstractAuditHandler.java @@ -117,7 +117,7 @@ public void addAuditEvent(User user, Container c, TableInfo table, @Nullable Aud { case INSERT: { - String newRecord = AbstractAuditTypeProvider.encodeForDataMap(c, row); + String newRecord = AbstractAuditTypeProvider.encodeForDataMap(row); if (newRecord != null) event.setNewRecordMap(newRecord, c); break; @@ -126,7 +126,7 @@ public void addAuditEvent(User user, Container c, TableInfo table, @Nullable Aud { if (existingRow.isEmpty()) { - String newRecord = AbstractAuditTypeProvider.encodeForDataMap(c, row); + String newRecord = AbstractAuditTypeProvider.encodeForDataMap(row); if (newRecord != null) event.setNewRecordMap(newRecord, c); } @@ -138,7 +138,7 @@ public void addAuditEvent(User user, Container c, TableInfo table, @Nullable Aud } case DELETE: { - String oldRecord = AbstractAuditTypeProvider.encodeForDataMap(c, row); + String oldRecord = AbstractAuditTypeProvider.encodeForDataMap(row); if (oldRecord != null) event.setOldRecordMap(oldRecord, c); break; @@ -177,11 +177,11 @@ private void setOldAndNewMapsForUpdate(DetailedAuditTypeEvent event, Container c // allow for adding fields that may be present in the updated row but not represented in the original row addDetailedModifiedFields(existingRow, modifiedRow, row); - String oldRecord = AbstractAuditTypeProvider.encodeForDataMap(c, originalRow); + String oldRecord = AbstractAuditTypeProvider.encodeForDataMap(originalRow); if (oldRecord != null) event.setOldRecordMap(oldRecord, c); - String newRecord = AbstractAuditTypeProvider.encodeForDataMap(c, modifiedRow); + String newRecord = AbstractAuditTypeProvider.encodeForDataMap(modifiedRow); if (newRecord != null) event.setNewRecordMap(newRecord, c); } diff --git a/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java b/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java index 2116b841e95..afc791fbf67 100644 --- a/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java +++ b/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java @@ -418,7 +418,7 @@ public static Map decodeFromDataMap(String properties) } } - public static String encodeForDataMap(@Nullable Container c, Map properties) + public static String encodeForDataMap(Map properties) { if (properties == null) return null; diff --git a/api/src/org/labkey/api/audit/SampleTimelineAuditEvent.java b/api/src/org/labkey/api/audit/SampleTimelineAuditEvent.java index 0b629dfb1b5..5c072c8a414 100644 --- a/api/src/org/labkey/api/audit/SampleTimelineAuditEvent.java +++ b/api/src/org/labkey/api/audit/SampleTimelineAuditEvent.java @@ -208,7 +208,7 @@ public void setOldRecordMap(String oldRecordMap, Container container) if (label != null) { row.put("samplestatelabel", label); - oldRecordMap = AbstractAuditTypeProvider.encodeForDataMap(container, row); + oldRecordMap = AbstractAuditTypeProvider.encodeForDataMap(row); } } super.setOldRecordMap(oldRecordMap); @@ -228,7 +228,7 @@ public void setNewRecordMap(String newRecordMap, Container container) if (label != null) { row.put("samplestatelabel", label); - newRecordMap = AbstractAuditTypeProvider.encodeForDataMap(container, row); + newRecordMap = AbstractAuditTypeProvider.encodeForDataMap(row); } } super.setNewRecordMap(newRecordMap, container); diff --git a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java index 997558a7d59..2297fdbb52c 100644 --- a/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/SampleTypeServiceImpl.java @@ -1249,7 +1249,7 @@ else if (event.getSampleLsid() != null) SampleTimelineAuditEvent.SampleTimelineEventType timelineEventType = SampleTimelineAuditEvent.SampleTimelineEventType.getTypeFromAction(action); if (timelineEventType != null) eventMetadata.put(SAMPLE_TIMELINE_EVENT_TYPE, action); - event.setMetadata(AbstractAuditTypeProvider.encodeForDataMap(c, eventMetadata)); + event.setMetadata(AbstractAuditTypeProvider.encodeForDataMap(eventMetadata)); } return event; @@ -1270,7 +1270,7 @@ private SampleTimelineAuditEvent createAuditRecord(Container container, String c event.setSampleTypeId(type.getRowId()); } event.setUserComment(userComment); - event.setMetadata(AbstractAuditTypeProvider.encodeForDataMap(container, metadata)); + event.setMetadata(AbstractAuditTypeProvider.encodeForDataMap(metadata)); return event; } @@ -1917,8 +1917,8 @@ public Map moveSamples(Collection sample newRecordMap.put(fileUpdateData.fieldName, fileUpdateData.targetFile.getAbsolutePath()); }); } - event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(targetContainer, oldRecordMap)); - event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(targetContainer, newRecordMap)); + event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(oldRecordMap)); + event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(newRecordMap)); AuditLogService.get().addEvent(user, event); } } diff --git a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java index 2140455c0cf..3d264d3a62b 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java @@ -882,7 +882,7 @@ public void addAuditEvent(@Nullable User user, Container container, Long domainE DomainPropertyAuditProvider.DomainPropertyAuditEvent event = new DomainPropertyAuditProvider.DomainPropertyAuditEvent(container, null, field.getName(), "Created", domainEventId, domainName, "Calculated field created."); - event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, field.getAuditRecordMap())); + event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(field.getAuditRecordMap())); AuditLogService.get().addEvent(user, event); } } @@ -905,8 +905,8 @@ public void addAuditEvent(@Nullable User user, Container container, Long domainE DomainPropertyAuditProvider.DomainPropertyAuditEvent event = new DomainPropertyAuditProvider.DomainPropertyAuditEvent(container, null, oldNew.first.getName(), "Modified", domainEventId, domainName, "Calculated field updated."); - event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, oldNew.first.getAuditRecordMap())); - event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, oldNew.second.getAuditRecordMap())); + event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(oldNew.first.getAuditRecordMap())); + event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(oldNew.second.getAuditRecordMap())); AuditLogService.get().addEvent(user, event); } } @@ -1080,8 +1080,8 @@ private Long addAuditEvent(@Nullable User user, String comment, @Nullable String if (user != null) { DomainAuditProvider.DomainAuditEvent event = new DomainAuditProvider.DomainAuditEvent(getContainer(), comment); - event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, oldProps)); - event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, newProps)); + event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(oldProps)); + event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(newProps)); event.setUserComment(auditUserComment); event.setDomainUri(getTypeURI()); @@ -1101,8 +1101,8 @@ private void addPropertyAuditEvent(@Nullable User user, Container container, Dom DomainPropertyAuditProvider.DomainPropertyAuditEvent event = new DomainPropertyAuditProvider.DomainPropertyAuditEvent(getContainer(), prop.getPropertyURI(), prop.getName(), action, domainEventId, domainName, changeSummary); - event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, oldProps)); - event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, newProps)); + event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(oldProps)); + event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(newProps)); AuditLogService.get().addEvent(user, event); } diff --git a/list/src/org/labkey/list/model/ListManager.java b/list/src/org/labkey/list/model/ListManager.java index f2000484fab..613b2c562b2 100644 --- a/list/src/org/labkey/list/model/ListManager.java +++ b/list/src/org/labkey/list/model/ListManager.java @@ -1237,7 +1237,7 @@ String formatAuditItem(ListDefinitionImpl list, User user, Map p } if (!recordChangedMap.isEmpty()) - itemRecord = ListAuditProvider.encodeForDataMap(list.getContainer(), recordChangedMap); + itemRecord = ListAuditProvider.encodeForDataMap(recordChangedMap); } return itemRecord; diff --git a/study/src/org/labkey/study/assay/StudyPublishManager.java b/study/src/org/labkey/study/assay/StudyPublishManager.java index 694d2e80881..3e14f6c7534 100644 --- a/study/src/org/labkey/study/assay/StudyPublishManager.java +++ b/study/src/org/labkey/study/assay/StudyPublishManager.java @@ -642,7 +642,7 @@ private void logPublishEvent(Dataset.PublishSource publishSource, ExpObject sour var timelineEventType = SampleTimelineAuditEvent.SampleTimelineEventType.PUBLISH; Map eventMetadata = new HashMap<>(); eventMetadata.put(SAMPLE_TIMELINE_EVENT_TYPE, timelineEventType.name()); - String metadata = AbstractAuditTypeProvider.encodeForDataMap(sourceContainer, eventMetadata); + String metadata = AbstractAuditTypeProvider.encodeForDataMap(eventMetadata); List sampleIds = rows.stream().map(m -> (Integer) m.get(StudyPublishService.ROWID_PROPERTY_NAME)).collect(toList()); List samples = ExperimentService.get().getExpMaterials(sampleIds); @@ -1597,7 +1597,7 @@ public void addRecallAuditEvent(Container sourceContainer, User user, Dataset de var timelineEventType = SampleTimelineAuditEvent.SampleTimelineEventType.RECALL; Map eventMetadata = new HashMap<>(); eventMetadata.put(SAMPLE_TIMELINE_EVENT_TYPE, timelineEventType.name()); - String metadata = AbstractAuditTypeProvider.encodeForDataMap(sourceContainer, eventMetadata); + String metadata = AbstractAuditTypeProvider.encodeForDataMap(eventMetadata); List sampleIds = pairs.stream().map(Pair::getValue).collect(toList()); List samples = ExperimentService.get().getExpMaterials(sampleIds); diff --git a/study/src/org/labkey/study/audit/ParticipantGroupAuditProvider.java b/study/src/org/labkey/study/audit/ParticipantGroupAuditProvider.java index 94d903e914f..d22a41c7c4d 100644 --- a/study/src/org/labkey/study/audit/ParticipantGroupAuditProvider.java +++ b/study/src/org/labkey/study/audit/ParticipantGroupAuditProvider.java @@ -220,8 +220,8 @@ public static ParticipantGroupAuditEvent categoryChange( var event = new ParticipantGroupAuditProvider.ParticipantGroupAuditEvent(c, comment, newCategory.getRowId()); if (prevCategory != null) - event.setOldRecordMap(createEncodedRecordMap(c, factory.toMap(prevCategory, null))); - event.setNewRecordMap(createEncodedRecordMap(c, factory.toMap(newCategory, null))); + event.setOldRecordMap(createEncodedRecordMap(factory.toMap(prevCategory, null))); + event.setNewRecordMap(createEncodedRecordMap(factory.toMap(newCategory, null))); return event; } @@ -248,10 +248,10 @@ public static ParticipantGroupAuditEvent groupChange( var event = new ParticipantGroupAuditProvider.ParticipantGroupAuditEvent(c, comment, newGroup.getCategoryId()); event.setParticipantGroup(newGroup.getRowId()); - event.setNewRecordMap(createEncodedRecordMap(c, factory.toMap(newGroup, null))); + event.setNewRecordMap(createEncodedRecordMap(factory.toMap(newGroup, null))); if (prevGroup != null) { - event.setOldRecordMap(createEncodedRecordMap(c, factory.toMap(prevGroup, null))); + event.setOldRecordMap(createEncodedRecordMap(factory.toMap(prevGroup, null))); if (event.getNewRecordMap().equals(event.getOldRecordMap())) return null; } @@ -282,7 +282,7 @@ public static ParticipantGroupAuditEvent participantDeleted( "The participant '" + participantId + "' was deleted", groupId); } - private static String createEncodedRecordMap(Container c, Map bean) + private static String createEncodedRecordMap(Map bean) { if (bean.containsKey("ownerId")) { @@ -299,7 +299,7 @@ private static String createEncodedRecordMap(Container c, Map be .filter(e -> _allowedFields.contains(e.getKey())) .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() != null ? e.getValue() : "")); - return AbstractAuditTypeProvider.encodeForDataMap(c, filteredMap); + return AbstractAuditTypeProvider.encodeForDataMap(filteredMap); } } } diff --git a/study/src/org/labkey/study/model/DatasetDefinition.java b/study/src/org/labkey/study/model/DatasetDefinition.java index 3476f268e59..bab733391ba 100644 --- a/study/src/org/labkey/study/model/DatasetDefinition.java +++ b/study/src/org/labkey/study/model/DatasetDefinition.java @@ -1805,28 +1805,28 @@ protected DatasetAuditProvider.DatasetAuditEvent createDetailedAuditRecord(User if (action==DELETE || action==TRUNCATE) { - oldRecordString = DatasetAuditProvider.encodeForDataMap(c, record); + oldRecordString = DatasetAuditProvider.encodeForDataMap(record); } else if (existingRecord != null && existingRecord.size() > 0) { Pair, Map> rowPair = AuditHandler.getOldAndNewRecordForMerge(record, existingRecord, Collections.emptySet(), tInfo == null? TableInfo.defaultExcludedDetailedUpdateAuditFields : tInfo.getExcludedDetailedUpdateAuditFields(), tInfo); - oldRecordString = DatasetAuditProvider.encodeForDataMap(c, rowPair.first); + oldRecordString = DatasetAuditProvider.encodeForDataMap(rowPair.first); // Check if no fields changed, if so adjust messaging if (rowPair.second.size() == 0 ) { auditComment = "Dataset row was processed, but no changes detected"; // Record values that were processed - newRecordString = DatasetAuditProvider.encodeForDataMap(c, record); + newRecordString = DatasetAuditProvider.encodeForDataMap(record); } else { - newRecordString = DatasetAuditProvider.encodeForDataMap(c, rowPair.second); + newRecordString = DatasetAuditProvider.encodeForDataMap(rowPair.second); } } else { - newRecordString = DatasetAuditProvider.encodeForDataMap(c, record); + newRecordString = DatasetAuditProvider.encodeForDataMap(record); } DatasetAuditProvider.DatasetAuditEvent event = new DatasetAuditProvider.DatasetAuditEvent(c, auditComment, _dataset.getDatasetId()); diff --git a/study/src/org/labkey/study/model/StudyManager.java b/study/src/org/labkey/study/model/StudyManager.java index 2d2c215af38..46a40cc75e2 100644 --- a/study/src/org/labkey/study/model/StudyManager.java +++ b/study/src/org/labkey/study/model/StudyManager.java @@ -2004,8 +2004,8 @@ public void updateDataQCState(Container container, User user, int datasetId, Col DatasetAuditProvider.DatasetAuditEvent event = new DatasetAuditProvider.DatasetAuditEvent(container, auditComment, datasetId); event.setHasDetails(true); - event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, oldQCStates)); - event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(container, newQCStates)); + event.setOldRecordMap(AbstractAuditTypeProvider.encodeForDataMap(oldQCStates)); + event.setNewRecordMap(AbstractAuditTypeProvider.encodeForDataMap(newQCStates)); AuditLogService.get().addEvent(user, event); clearCaches(container, false); From a44b1ce87e933de8888527e47f9080cbef8e920c Mon Sep 17 00:00:00 2001 From: XingY Date: Thu, 22 May 2025 22:22:10 -0700 Subject: [PATCH 29/30] Merge branch 'develop' into fb_domainAudit # Conflicts: # biologics/package-lock.json # biologics/package.json # inventory/package-lock.json # inventory/package.json # sampleManagement/package-lock.json # sampleManagement/package.json --- core/package-lock.json | 16 ++++++++-------- experiment/package-lock.json | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/core/package-lock.json b/core/package-lock.json index 74425b5f87b..2334a475017 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -8,7 +8,7 @@ "name": "labkey-core", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.43.0", + "@labkey/components": "6.43.1-fb-domainAudit.1", "@labkey/themes": "1.4.2" }, "devDependencies": { @@ -2987,9 +2987,9 @@ } }, "node_modules/@labkey/api": { - "version": "1.41.0", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.41.0.tgz", - "integrity": "sha512-FWhELnNLkTVNNGXOnPHFoLb/CvPKVnibvVnINGETh1rBUmp8UdGrSV2OuhhPEkuGoi/SGp2zI3dkkSYjRR20Eg==" + "version": "1.41.1", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.41.1.tgz", + "integrity": "sha512-5CaGXA0Z2m/D7lDf8F8gZugQVxcWURpRsEdU+Xc/xn4Vz5ZdLjfgzyD87WsEtwNEi0Ed8Lw/BpsToFh80g+3IA==" }, "node_modules/@labkey/build": { "version": "8.5.0", @@ -3029,12 +3029,12 @@ } }, "node_modules/@labkey/components": { - "version": "6.43.0", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.43.0.tgz", - "integrity": "sha512-hAUXKcaTnsZmh95DgcbZU+Ch62R/Cd1rWLECLt78w/7Db3O19P4O5VWFtO+Uxu5+5s2q1YhDIbthMYGRlR3uSg==", + "version": "6.43.1-fb-domainAudit.1", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.43.1-fb-domainAudit.1.tgz", + "integrity": "sha512-c/xiI4VZBWpEyhNFCSN8j/6TYq5U2AcnkF0Q+QNEVyE8oUEk4DVQkvVM69a2cIt1XUVpuVrS1D8btsj+ioNycw==", "dependencies": { "@hello-pangea/dnd": "18.0.1", - "@labkey/api": "1.41.0", + "@labkey/api": "1.41.1", "@testing-library/dom": "~10.4.0", "@testing-library/jest-dom": "~6.6.3", "@testing-library/react": "~16.3.0", diff --git a/experiment/package-lock.json b/experiment/package-lock.json index ac25d613f48..a858b6c44a3 100644 --- a/experiment/package-lock.json +++ b/experiment/package-lock.json @@ -8,7 +8,7 @@ "name": "experiment", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.43.0" + "@labkey/components": "6.43.1-fb-domainAudit.1" }, "devDependencies": { "@labkey/build": "8.5.0", @@ -2800,9 +2800,9 @@ } }, "node_modules/@labkey/api": { - "version": "1.41.0", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.41.0.tgz", - "integrity": "sha512-FWhELnNLkTVNNGXOnPHFoLb/CvPKVnibvVnINGETh1rBUmp8UdGrSV2OuhhPEkuGoi/SGp2zI3dkkSYjRR20Eg==" + "version": "1.41.1", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.41.1.tgz", + "integrity": "sha512-5CaGXA0Z2m/D7lDf8F8gZugQVxcWURpRsEdU+Xc/xn4Vz5ZdLjfgzyD87WsEtwNEi0Ed8Lw/BpsToFh80g+3IA==" }, "node_modules/@labkey/build": { "version": "8.5.0", @@ -2842,12 +2842,12 @@ } }, "node_modules/@labkey/components": { - "version": "6.43.0", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.43.0.tgz", - "integrity": "sha512-hAUXKcaTnsZmh95DgcbZU+Ch62R/Cd1rWLECLt78w/7Db3O19P4O5VWFtO+Uxu5+5s2q1YhDIbthMYGRlR3uSg==", + "version": "6.43.1-fb-domainAudit.1", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.43.1-fb-domainAudit.1.tgz", + "integrity": "sha512-c/xiI4VZBWpEyhNFCSN8j/6TYq5U2AcnkF0Q+QNEVyE8oUEk4DVQkvVM69a2cIt1XUVpuVrS1D8btsj+ioNycw==", "dependencies": { "@hello-pangea/dnd": "18.0.1", - "@labkey/api": "1.41.0", + "@labkey/api": "1.41.1", "@testing-library/dom": "~10.4.0", "@testing-library/jest-dom": "~6.6.3", "@testing-library/react": "~16.3.0", From 7de2e2014be405a6342eb343f797a21a0833f3b3 Mon Sep 17 00:00:00 2001 From: XingY Date: Fri, 23 May 2025 17:49:58 -0700 Subject: [PATCH 30/30] publish --- core/package-lock.json | 8 ++++---- core/package.json | 2 +- experiment/package-lock.json | 8 ++++---- experiment/package.json | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/package-lock.json b/core/package-lock.json index 018ac12ddeb..82c15dada19 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -8,7 +8,7 @@ "name": "labkey-core", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.43.2-fb-domainAudit.1", + "@labkey/components": "6.43.2", "@labkey/themes": "1.4.2" }, "devDependencies": { @@ -3029,9 +3029,9 @@ } }, "node_modules/@labkey/components": { - "version": "6.43.2-fb-domainAudit.1", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.43.2-fb-domainAudit.1.tgz", - "integrity": "sha512-63gRLu1dGGeXNuaXJ6f/whLqscFLlD4diG/ls70K7tdUF4PLHekBvzqfYpjUljnc1CZOBO2S0jxyVgxHmkRukQ==", + "version": "6.43.2", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.43.2.tgz", + "integrity": "sha512-f2rv0XIDCRW44CWM1wFik3PMLqBgmmpr50FIVH9FApL+CK5hDK+MNx5gkad3OjWMeOkFq9KD4bIOAPFJIrTMEw==", "dependencies": { "@hello-pangea/dnd": "18.0.1", "@labkey/api": "1.41.1", diff --git a/core/package.json b/core/package.json index 9045504b3cb..141e42fd0e0 100644 --- a/core/package.json +++ b/core/package.json @@ -53,7 +53,7 @@ } }, "dependencies": { - "@labkey/components": "6.43.2-fb-domainAudit.1", + "@labkey/components": "6.43.2", "@labkey/themes": "1.4.2" }, "devDependencies": { diff --git a/experiment/package-lock.json b/experiment/package-lock.json index e429f8ad7b1..7a0b65181e8 100644 --- a/experiment/package-lock.json +++ b/experiment/package-lock.json @@ -8,7 +8,7 @@ "name": "experiment", "version": "0.0.0", "dependencies": { - "@labkey/components": "6.43.2-fb-domainAudit.1" + "@labkey/components": "6.43.2" }, "devDependencies": { "@labkey/build": "8.5.0", @@ -2842,9 +2842,9 @@ } }, "node_modules/@labkey/components": { - "version": "6.43.2-fb-domainAudit.1", - "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.43.2-fb-domainAudit.1.tgz", - "integrity": "sha512-63gRLu1dGGeXNuaXJ6f/whLqscFLlD4diG/ls70K7tdUF4PLHekBvzqfYpjUljnc1CZOBO2S0jxyVgxHmkRukQ==", + "version": "6.43.2", + "resolved": "https://labkey.jfrog.io/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-6.43.2.tgz", + "integrity": "sha512-f2rv0XIDCRW44CWM1wFik3PMLqBgmmpr50FIVH9FApL+CK5hDK+MNx5gkad3OjWMeOkFq9KD4bIOAPFJIrTMEw==", "dependencies": { "@hello-pangea/dnd": "18.0.1", "@labkey/api": "1.41.1", diff --git a/experiment/package.json b/experiment/package.json index 06a774ad39d..a46d2da1c0b 100644 --- a/experiment/package.json +++ b/experiment/package.json @@ -13,7 +13,7 @@ "test-integration": "cross-env NODE_ENV=test jest --ci --runInBand -c test/js/jest.config.integration.js" }, "dependencies": { - "@labkey/components": "6.43.2-fb-domainAudit.1" + "@labkey/components": "6.43.2" }, "devDependencies": { "@labkey/build": "8.5.0",