kestra-io / kestra-io/plugin-datahub

Ingestion: a `kestra://` recipe URI supplied as a string is unreachable and throws `ClassCastException`

Open
#69 0 comments 0 reactions 0 assignees View on GitHub
area/plugin good first issue
Dominant language
Java
Stars
1
Forks
3
Avg merge
13h 42m
Merged PRs (30d)
3

Description

### Describe the issue

## Summary

`Ingestion` documents `recipe` as accepting either an inline map or a `kestra://` internal-storage URI pointing to a recipe file. But the URI form only works when `recipe` is a `java.net.URI` *object*; a `kestra://…` **string** — the only form a YAML flow can produce — falls through to a `(Map) recipe` cast and throws `ClassCastException` before any ingestion runs. `recipe` is also never rendered.

Found during a documentation audit; docs PR: kestra-io/plugin-datahub#65

## Details

**`src/main/java/io/kestra/plugin/datahub/Ingestion.java`**

`run()` calls `getRecipe()` as its first statement, and `getRecipe()` only treats a `URI` object as a storage URI:

```java
private String getRecipe(RunContext runContext) throws Exception {
File tempFile = runContext.workingDir().createTempFile(".yml").toFile();
Map yaml;
if (this.recipe instanceof URI from) { // only a java.net.URI OBJECT
...
yaml = MAPPER.readValue(runContext.storage().getFile(from), ...);
} else {
yaml = (Map) recipe; // String -> ClassCastException
}
...
}
```

- `recipe` (L113-115) is `@PluginProperty(group = "main")` **without** `dynamic = true`, and is never `runContext.render(this.recipe)`-ed.
- A YAML value like `recipe: "{{ outputs.write.uri }}"` or `recipe: "{{ input('recipe_file') }}"` deserializes to a `String`, not a `URI`, so it hits the `else` branch and throws `ClassCastException`.

## Impact

- The documented "recipe as a `kestra://` URI / recipe file" mode is unreachable from a normal flow — it fails with a raw `ClassCastException`.
- Because `recipe` is not `dynamic` and never rendered, Pebble expressions passed as/within `recipe` (including secrets embedded in an inline recipe map) may not be resolved.

## Suggested fix

- In `getRecipe()`, detect a `kestra://` **string** prefix and read it from internal storage (as the sibling plugins do), in addition to the `URI` object case.
- Consider marking `recipe` `dynamic = true` and rendering it so templated values resolve.

## Verification

Reproduced by a unit test that passes `recipe` as a `kestra://` string (what YAML produces) and asserts the `ClassCastException` — no DocumentDB/Docker required, throws in `getRecipe()` before any external call. Passes against current `main`:

```java
package io.kestra.plugin.datahub;

import com.google.common.collect.ImmutableMap;
import io.kestra.core.junit.annotations.KestraTest;
import io.kestra.core.runners.RunContext;
import io.kestra.core.runners.RunContextFactory;
import io.kestra.core.utils.TestsUtils;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertThrows;

@KestraTest
class DocReviewBugVerificationTest {

@Inject
private RunContextFactory runContextFactory;

@Test
void recipeAsKestraUriString_throwsClassCastException() {
Ingestion task = Ingestion.builder()
.id("unit-test")
.type(Ingestion.class.getName())
.recipe("kestra:///some/namespace/recipe.yml") // String, as YAML renders a kestra:// URI to
.build();

RunContext runContext = TestsUtils.mockRunContext(runContextFactory, task, ImmutableMap.of());

ClassCastException exception = assertThrows(ClassCastException.class, () -> task.run(runContext));
assertThat(exception.getMessage(), containsString("String"));
}
}
```

Note: the existing `IngestionTest.runWithRecipeFile` passes a `URI` *object* returned by `storageInterface.put(...)`, which is why the "recipe file" path works in tests but not from a real flow.

### Environment

- Kestra Version: develop

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with src/main/java/io/kestra/plugin/datahub/Ingestion.java, especially getRecipe() and the recipe property around L113-L115. Compare the existing IngestionTest.runWithRecipeFile path with the supplied DocReviewBugVerificationTest, then verify that a YAML-produced kestra:// string no longer reaches the Map cast and that the existing URI-object test still passes.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.