Skip to content

Conversation

@GBirkel
Copy link
Contributor

@GBirkel GBirkel commented Sep 9, 2025

This is a cleaned-up version of an earlier draft PR ( #2097 ).

Description

Add a field for generating dataset links based on admin-configured templates.

See #689 for details.

This has a companion PR for the front-end, which is still in draft form: SciCatProject/frontend#1910

Motivation

We get a lot of requests from users and researchers to provide a variety of different types of web-based tools that related directly to datasets. These can be pages that display visualization, pages that provide analysis tools (including AI/ML) and jupyter notebooks on particular JupyterHub instance. In all cases, we would want to take the user to the page with the dataset in context.

Changes:

  • New API endpoint: Datasets/[pid]/externallinks

This returns an array of objects, { url: string, title: string, description: string }. See ExternalLinkClass in the code for more detail.

  • New configuration entry, parsed from file datasetExternalLinkTemplates.json with an example template file.

Example of the content of this file:

[
  {
    "title": "Franzviewer II",
    "url_template": "https://franz.site.com/franzviewer?id=${dataset.pid}",
    "description_template": "View ${dataset.numberOfFiles} files in Franz' own personal viewer",
    "filter": "(dataset.type == 'derived') && dataset.owner.includes('Franz')"
  },
  {
    "title": "High Beam-Energy View",
    "url_template": "https://beamviewer.beamline.net/highenergy?id=${dataset.pid}",
    "description_template": "The high-energy beamviewer (value ${dataset.scientificMetadata?.beamEnergy?.value}) at beamCo",
    "filter": "(dataset.scientificMetadata?.beamEnergy?.value > 20)"
  }
]

The filter field is JavaScript, parsed into a function at run time, which is called with a dataset argument containing a full dataset record. If the function returns true, the given link applies to the given dataset.

The url_template and description_template fields are JavaScript-style format strings, which are passed the same dataset argument. The strings they emit go into the url and description objects returned by the API.

where: { datasetId: outputDataset.pid },
});
break;
if (includeFilters) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you care to elaborate on this change a bit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I came across this code when I was implementing an early version of this feature that included the external links in the dataset record itself, and found it confusing. The change I made is logically equivalent but easier to parse.

return await this.convertCurrentToObsoleteSchema(outputDatasetDto);
}

// GET /datasets/:id/externallinks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add new endpoints to the old v3 controller?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the right move is, honestly.
I'd be happy to add them but it would of course be a change to the v3 API (though only an additive one). Are we still changing that API or is it in a static state now that we have v4?

throw new NotFoundException(`Dataset #${id} not found`);
}

interface ExternalLinkTemplateConfig {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nice to have interfaces/types in separate file, but that it just my subjective view.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general I agree. Right now this type is only used once, and in this spot. But I think we should be using types like this when we're parsing/validating all the JSON that this config data comes from. That may be a task best done en-masse though, as a separate change...

}

return templates
.filter((d) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a more descriptive variable name here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Addressed in 47838fe .

// so there is no equivalent schema representation for it.

export class ExternalLinkClass {
@ApiProperty({
Copy link
Member

@emigun emigun Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could consider having these api properties automatically generated as we for example did in the samples module (if I remember correctly)

let me know if you want more information

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean by using SchemaFactory? I don't think we can do that here because these properties have no representations in MongoDb ... But I could be wrong. I'd love more information!

@emigun
Copy link
Member

emigun commented Sep 17, 2025

I think tests are missing?

@minottic minottic marked this pull request as draft November 3, 2025 13:51
@minottic
Copy link
Member

minottic commented Nov 3, 2025

@GBirkel I have converted this to draft, as it did not have commits for the past month. Feel free to move it back to "ready for review" once you feel so or if I shouldn't have moved to draft

Merge branch 'refs/heads/master' into templated-external-links-v3
@GBirkel
Copy link
Contributor Author

GBirkel commented Nov 5, 2025

@GBirkel I have converted this to draft, as it did not have commits for the past month. Feel free to move it back to "ready for review" once you feel so or if I shouldn't have moved to draft

Thanks. Yeah, I got sidetracked by other activity at the ALS. I think you're right that it needs tests before I can call it ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants