-
Notifications
You must be signed in to change notification settings - Fork 7
Issue 695: Add feature to allow unreferenced files to be deleted from apps #7320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release25.11-SNAPSHOT
Are you sure you want to change the base?
Changes from all commits
9034063
a2ffc20
f51bd73
7350ecc
9031881
dbc018d
e103580
8d84c5c
82c8247
6bec4b5
bdb8893
62ac8de
2a78a9d
4b7da07
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| package org.labkey.api.exp.query; | ||
|
|
||
| import org.apache.commons.lang3.StringUtils; | ||
| import org.jetbrains.annotations.NotNull; | ||
| import org.labkey.api.data.BaseColumnInfo; | ||
| import org.labkey.api.data.ContainerFilter; | ||
| import org.labkey.api.data.CoreSchema; | ||
| import org.labkey.api.data.JdbcType; | ||
| import org.labkey.api.data.SQLFragment; | ||
| import org.labkey.api.data.TableInfo; | ||
| import org.labkey.api.data.VirtualTable; | ||
| import org.labkey.api.exp.api.ExperimentService; | ||
| import org.labkey.api.files.FileContentService; | ||
| import org.labkey.api.query.FilteredTable; | ||
| import org.labkey.api.query.UserIdQueryForeignKey; | ||
| import org.labkey.api.query.UserSchema; | ||
| import org.labkey.api.query.column.BuiltInColumnTypes; | ||
|
|
||
| public class ExpUnreferencedSampleFilesTable extends FilteredTable<ExpSchema> | ||
| { | ||
| public ExpUnreferencedSampleFilesTable(@NotNull ExpSchema schema, ContainerFilter cf) | ||
| { | ||
| super(createVirtualTable(schema), schema, cf); | ||
| setDescription("Contains all sample files that are not referenced by any domain fields."); | ||
| wrapAllColumns(true); | ||
| } | ||
|
|
||
| private static TableInfo createVirtualTable(@NotNull ExpSchema schema) | ||
| { | ||
| return new ExpUnreferencedSampleFilesTable.FileUnionTable(schema); | ||
| } | ||
|
|
||
| private static class FileUnionTable extends VirtualTable | ||
| { | ||
| private final SQLFragment _query; | ||
|
|
||
| public FileUnionTable(@NotNull UserSchema schema) | ||
| { | ||
| super(CoreSchema.getInstance().getSchema(), ExpSchema.SAMPLE_FILES_TABLE, schema); | ||
|
|
||
| FileContentService svc = FileContentService.get(); | ||
|
|
||
| _query = new SQLFragment(); | ||
| if (svc == null) | ||
| return; | ||
| _query.appendComment("<SampleFileListTableInfo>", getSchema().getSqlDialect()); | ||
|
|
||
| TableInfo expDataTable = ExperimentService.get().getTinfoData(); | ||
| TableInfo materialTable = ExperimentService.get().getTinfoMaterial(); | ||
|
|
||
| SQLFragment listQuery = svc.listSampleFilesQuery(schema.getUser()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider moving this check up to avoid writing an open comment to the query. super(CoreSchema.getInstance().getSchema(), ExpSchema.SAMPLE_FILES_TABLE, schema);
_query = new SQLFragment();
FileContentService svc = FileContentService.get();
if (svc == null)
return;
SQLFragment listQuery = svc.listSampleFilesQuery(schema.getUser());
if (StringUtils.isEmpty(listQuery))
return;
_query.appendComment("<SampleFileListTableInfo>", getSchema().getSqlDialect());
// ... |
||
| if (StringUtils.isEmpty(listQuery)) | ||
| return; | ||
|
|
||
| SQLFragment sampleFileSql = new SQLFragment("SELECT m.Container, if.FilePathShort \n") | ||
| .append("FROM (") | ||
| .append(svc.listSampleFilesQuery(schema.getUser())) | ||
| .append(") AS if \n") | ||
| .append("JOIN ") | ||
| .append(materialTable, "m") | ||
| .append(" ON if.SourceKey = m.RowId"); | ||
|
|
||
| SQLFragment unreferencedFileSql = new SQLFragment("SELECT ed.rowId, ed.name as filename, ed.container, ed.created, ed.createdBy, ed.DataFileUrl FROM ") | ||
| .append(expDataTable, "ed") | ||
| .append(" LEFT JOIN (") | ||
| .append(sampleFileSql) | ||
| .append(" ) sf\n") | ||
| .append(" ON ed.name = sf.FilePathShort AND ed.container = sf.container\n") | ||
| .append(" WHERE ed.datafileurl LIKE ") | ||
| .appendValue("%@files/sampletype/%") | ||
| .append(" AND sf.FilePathShort IS NULL"); | ||
|
|
||
| _query.append(unreferencedFileSql); | ||
|
|
||
| _query.appendComment("</SampleFileListTableInfo>", getSchema().getSqlDialect()); | ||
|
|
||
| var rowIdCol = new BaseColumnInfo("RowId", this, JdbcType.INTEGER); | ||
| rowIdCol.setHidden(true); | ||
| rowIdCol.setKeyField(true); | ||
| addColumn(rowIdCol); | ||
|
|
||
| var fileNameCol = new BaseColumnInfo("FileName", this, JdbcType.VARCHAR); | ||
| addColumn(fileNameCol); | ||
|
|
||
| if (schema.getUser().hasApplicationAdminPermission()) | ||
| { | ||
| var filePathCol = new BaseColumnInfo("DataFileUrl", this, JdbcType.VARCHAR); | ||
| filePathCol.setHidden(true); | ||
| addColumn(filePathCol); | ||
| } | ||
|
|
||
| var containerCol = new BaseColumnInfo("Container", this, JdbcType.VARCHAR); | ||
| containerCol.setConceptURI(BuiltInColumnTypes.CONTAINERID_CONCEPT_URI); | ||
| addColumn(containerCol); | ||
|
|
||
| var createdCol = new BaseColumnInfo("Created", this, JdbcType.DATE); | ||
| addColumn(createdCol); | ||
|
|
||
| var createdByCol = new BaseColumnInfo("CreatedBy", this, JdbcType.INTEGER); | ||
| createdByCol.setFk(new UserIdQueryForeignKey(getUserSchema(), true)); | ||
| addColumn(createdByCol); | ||
| } | ||
|
|
||
| @NotNull | ||
| @Override | ||
| public SQLFragment getFromSQL() | ||
| { | ||
| return _query; | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -94,4 +94,9 @@ default void fileDeleted(@NotNull Path deleted, @Nullable User user, @Nullable C | |
| * </ul> | ||
| */ | ||
| SQLFragment listFilesQuery(); | ||
|
|
||
| default SQLFragment listSampleFilesQuery() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Annotate with |
||
| { | ||
| return null; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |
| import org.apache.commons.lang3.math.NumberUtils; | ||
| import org.jetbrains.annotations.NotNull; | ||
| import org.jetbrains.annotations.Nullable; | ||
| import org.json.JSONObject; | ||
| import org.labkey.api.admin.FolderSerializationRegistry; | ||
| import org.labkey.api.assay.AssayProvider; | ||
| import org.labkey.api.assay.AssayService; | ||
|
|
@@ -112,6 +113,7 @@ | |
| import org.labkey.api.vocabulary.security.DesignVocabularyPermission; | ||
| import org.labkey.api.webdav.WebdavResource; | ||
| import org.labkey.api.webdav.WebdavService; | ||
| import org.labkey.api.writer.ContainerUser; | ||
| import org.labkey.experiment.api.DataClassDomainKind; | ||
| import org.labkey.experiment.api.ExpDataClassImpl; | ||
| import org.labkey.experiment.api.ExpDataClassTableImpl; | ||
|
|
@@ -181,6 +183,7 @@ | |
| import static org.labkey.api.data.ColumnRenderPropertiesImpl.STORAGE_UNIQUE_ID_CONCEPT_URI; | ||
| import static org.labkey.api.data.ColumnRenderPropertiesImpl.TEXT_CHOICE_CONCEPT_URI; | ||
| import static org.labkey.api.exp.api.ExperimentService.MODULE_NAME; | ||
| import static org.labkey.api.exp.query.ExpSchema.SAMPLE_FILES_TABLE; | ||
|
|
||
| public class ExperimentModule extends SpringModule | ||
| { | ||
|
|
@@ -266,6 +269,9 @@ protected void init() | |
| } | ||
| else | ||
| { | ||
| OptionalFeatureService.get().addExperimentalFeatureFlag(SAMPLE_FILES_TABLE, "Manage Unreferenced Sample Files", | ||
| "Enable 'Unreferenced Sample Files' table to view and delete sample files that are no longer referenced by samples", false); | ||
|
|
||
| OptionalFeatureService.get().addExperimentalFeatureFlag(NameGenerator.EXPERIMENTAL_ALLOW_GAP_COUNTER, "Allow gap with withCounter and rootSampleCount expression", | ||
| "Check this option if gaps in the count generated by withCounter or rootSampleCount name expression are allowed.", true); | ||
| } | ||
|
|
@@ -1117,4 +1123,12 @@ public Collection<String> getProvisionedSchemaNames() | |
| { | ||
| return PageFlowUtil.set(DataClassDomainKind.PROVISIONED_SCHEMA_NAME, SampleTypeDomainKind.PROVISIONED_SCHEMA_NAME); | ||
| } | ||
|
|
||
| @Override | ||
| public JSONObject getPageContextJson(ContainerUser context) | ||
| { | ||
| JSONObject json = new JSONObject(getDefaultPageContextJson(context.getContainer())); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Call super instead @Override
public JSONObject getPageContextJson(ContainerUser context)
{
JSONObject json = super.getPageContextJson(context);
json.put(SAMPLE_FILES_TABLE, OptionalFeatureService.get().isFeatureEnabled(SAMPLE_FILES_TABLE));
return json;
} |
||
| json.put(SAMPLE_FILES_TABLE, OptionalFeatureService.get().isFeatureEnabled(SAMPLE_FILES_TABLE)); | ||
| return json; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -275,7 +275,7 @@ public SQLFragment listFilesQuery(boolean skipCreatedModified) | |
| return listFilesQuery(skipCreatedModified, null); | ||
| } | ||
|
|
||
| public SQLFragment listFilesQuery(boolean skipCreatedModified, String filePath) | ||
| public SQLFragment listFilesQuery(boolean skipCreatedModified, CharSequence filePath) | ||
| { | ||
| final SQLFragment frag = new SQLFragment(); | ||
|
|
||
|
|
@@ -298,8 +298,11 @@ public SQLFragment listFilesQuery(boolean skipCreatedModified, String filePath) | |
| frag.append("WHERE\n"); | ||
| if (StringUtils.isEmpty(filePath)) | ||
| frag.append(" op.StringValue IS NOT NULL AND\n"); | ||
| else if (filePath instanceof SQLFragment) | ||
| frag.append(" op.StringValue = ").append(filePath).append(" AND\n"); | ||
| else | ||
| frag.append(" op.StringValue = ").appendStringLiteral(filePath, OntologyManager.getTinfoObject().getSqlDialect()).append(" AND\n"); | ||
|
|
||
| frag.append(" o.ObjectId = op.ObjectId AND\n"); | ||
| frag.append(" PropertyId IN (\n"); | ||
| frag.append(" SELECT PropertyId\n"); | ||
|
|
@@ -311,7 +314,26 @@ public SQLFragment listFilesQuery(boolean skipCreatedModified, String filePath) | |
| SQLFragment containerFrag = new SQLFragment("?", containerId); | ||
| TableUpdaterFileListener updater = new TableUpdaterFileListener(table, pathColumn.getColumnName(), TableUpdaterFileListener.Type.filePath, null, containerFrag); | ||
| frag.append("UNION").append(StringUtils.isEmpty(filePath) ? "" : " ALL" /*keep duplicate*/).append("\n"); | ||
| frag.append(updater.listFilesQuery(skipCreatedModified, filePath)); | ||
| frag.append(updater.listFilesQuery(skipCreatedModified, filePath, false)); | ||
| }); | ||
|
|
||
| return frag; | ||
| } | ||
|
|
||
| @Override | ||
| public SQLFragment listSampleFilesQuery() | ||
| { | ||
| final SQLFragment frag = new SQLFragment(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this return Or switch the check to be |
||
|
|
||
| hardTableFileLinkColumns((schema, table, pathColumn, containerId, domainUri) -> { | ||
| if (schema.getName().equals("expsampleset")) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use case-insensitive check against hardTableFileLinkColumns((schema, table, pathColumn, containerId, domainUri) -> {
if (PROVISIONED_SCHEMA_NAME.equalsIgnoreCase(schema.getName()))
{
// ...
}
}); |
||
| { | ||
| SQLFragment containerFrag = new SQLFragment("?", containerId); | ||
| TableUpdaterFileListener updater = new TableUpdaterFileListener(table, pathColumn.getColumnName(), TableUpdaterFileListener.Type.filePath, "rowid", containerFrag); | ||
| if (!frag.isEmpty()) | ||
| frag.append("UNION").append("").append("\n"); | ||
| frag.append(updater.listFilesQuery(true, null, true)); | ||
| } | ||
| }); | ||
|
|
||
| return frag; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps
extends VirtualTable<ExpSchema>and thenpublic FileUnionTable(@NotNull ExpSchema schema)?