{"id":32388687,"url":"https://github.com/data-integrations/image-directives","last_synced_at":"2025-10-25T03:55:09.058Z","repository":{"id":77753831,"uuid":"99382796","full_name":"data-integrations/image-directives","owner":"data-integrations","description":"A set of directives for working with images","archived":false,"fork":false,"pushed_at":"2025-08-13T16:54:30.000Z","size":1396,"stargazers_count":0,"open_issues_count":3,"forks_count":1,"subscribers_count":4,"default_branch":"develop","last_synced_at":"2025-08-13T18:35:44.750Z","etag":null,"topics":["cask-marketplace","cdap","cdap-dataprep","cdap-udds","data-prep","directives"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/data-integrations.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-08-04T22:05:50.000Z","updated_at":"2025-08-13T16:14:27.000Z","dependencies_parsed_at":"2025-08-18T22:01:32.977Z","dependency_job_id":null,"html_url":"https://github.com/data-integrations/image-directives","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/data-integrations/image-directives","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/data-integrations%2Fimage-directives","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/data-integrations%2Fimage-directives/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/data-integrations%2Fimage-directives/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/data-integrations%2Fimage-directives/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/data-integrations","download_url":"https://codeload.github.com/data-integrations/image-directives/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/data-integrations%2Fimage-directives/sbom","scorecard":{"id":324059,"data":{"date":"2025-08-11","repo":{"name":"github.com/data-integrations/image-directives","commit":"6378851b1a90f1ca5ce15d53273e15119b187ebe"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.9,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":5,"reason":"Found 8/14 approved changesets -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":1,"reason":"2 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: topLevel 'actions' permission set to 'read': .github/workflows/build-report.yml:25","Warn: topLevel 'statuses' permission set to 'write': .github/workflows/build-report.yml:26","Warn: topLevel 'checks' permission set to 'write': .github/workflows/build-report.yml:27","Warn: no topLevel permission defined: .github/workflows/build.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":7,"reason":"dependency not pinned by hash detected -- score normalized to 7","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-report.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/data-integrations/image-directives/build-report.yml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/data-integrations/image-directives/build.yml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:45: update your workflow using https://app.stepsecurity.io/secureworkflow/data-integrations/image-directives/build.yml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:54: update your workflow using https://app.stepsecurity.io/secureworkflow/data-integrations/image-directives/build.yml/develop?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   3 out of   3 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 16 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T02:04:09.988Z","repository_id":77753831,"created_at":"2025-08-18T02:04:09.997Z","updated_at":"2025-08-18T02:04:09.997Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280901444,"owners_count":26410586,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-25T02:00:06.499Z","response_time":81,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cask-marketplace","cdap","cdap-dataprep","cdap-udds","data-prep","directives"],"created_at":"2025-10-25T03:55:03.600Z","updated_at":"2025-10-25T03:55:09.044Z","avatar_url":"https://github.com/data-integrations.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Image Directives\n\n[![cm-available](https://cdap-users.herokuapp.com/assets/cm-available.svg)](https://docs.cdap.io/cdap/current/en/integrations/cask-market.html)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Join CDAP community](https://cdap-users.herokuapp.com/badge.svg?t=wrangler)](https://cdap-users.herokuapp.com?t=1)\n\nA set of directives to allow users to work with images.\n\n## Introduction\n\nCDAP provides extensive support for user defined directives (UDDs) as a way to specify custom processing for DataPrep in CDAP. CDAP UDDs can currently be implemented in Java.\n\nCDAP provides the most extensive support for Java functions. Java functions are also more efficient because they are implemented in the same language as CDAP and DataPrep and, also because additional interfaces and integrations with other CDAP subsystems are supported.\n\nUser Defined Directives, also known as UDDs, allow you to create custom functions to transform records within CDAP DataPrep a.k.a Wrangler. CDAP comes with a comprehensive library of functions. There are however some omissions, and some specific cases for which UDDs are the solution.\n\nUDDs, similar to User-defined Functions (UDFs) have a long history of usefulness in SQL-derived languages and other data processing and query systems.  While the framework can be rich in their expressiveness, there is just no way they can anticipate all the things a developer wants to do.  Thus, the custom UDF has become commonplace in our data manipulation toolbox. In order to support customization or extension, CDAP now has the ability to build your own functions for manipulating data through UDDs.\n\nDeveloping CDAP DataPrep UDDs is by no means any rocket science, and is an effective way of solving problems that could either be downright impossible, or does not meet your requirements or very akward to solve. \n\n## Developing UDD\n\nThere is one simple interface for developing your customized directive. The simple interface `io.cdap.wrangler.api.Directive` can be used for developing user defined directive.\n\n### Simple API\n\nBuilding a UDD with the simpler UDD API involves nothing more than writing a class with three functions (evaluate) and few annotations. Here is an example:\n\n```\n@Plugin(type = UDD.Type)\n@Name(SimpleUDD.NAME)\n@Categories(categories = { \"example\", \"simple\" })\n@Description(\"My first simple user defined directive\")\npublic SimpleUDD implements Directive {\n  public static final String NAME = \"my-simple-udd\";\n  \n  public UsageDefinition define() {\n    ...\n  }\n  \n  public void initialize(Arguments args) throws DirectiveParseException {\n    ...\n  }\n  \n  public List\u003cRow\u003e execute(List\u003cRow\u003e rows, RecipeContext context) throws RecipeException, ErrorRowException {\n    ...\n  }\n}\n```\n\n### Testing a simple UDD\n\nBecause the UDD is a simple three functions class, you can test it with regular testing tools, like JUnit.\n\n```\npublic class SimpleUDDTest {\n\n  @Test\n  public void testSimpleUDD() throws Exception {\n    TestRecipe recipe = new TestRecipe();\n    recipe(\"parse-as-csv :body ',';\");\n    recipe(\"drop :body;\");\n    recipe(\"rename :body_1 :simpledata;\");\n    recipe(\"!my-simple-udd ...\");\n    \n    TestRows rows = new TestRows();\n    rows.add(new Row(\"body\", \"root,joltie,mars avenue\"));\n    RecipePipeline pipeline = TestingRig.pipeline(RowHash.class, recipe);\n    List\u003cRow\u003e actual = pipeline.execute(rows.toList());\n  }\n}\n```\n\n### Building a UDD Plugin\n\nThere is nothing much to be done here - this example repository includes a maven POM file that is pre-configured for building the directive JAR. All that a developer does is build the project using the following command. \n\n```\n  mvn clean package\n```\n\nThis would generate two files\n\n  * Artifact - `my-simple-udd-1.0-SNAPSHOT.jar`\n  * Artifact Configuration `my-simple-udd-1.0-SNAPSHOT.json`\n  \n### Deploying Plugin\n\nThere are multiple ways in which the custom directive can be deployed to CDAP. The two popular ways are through using CDAP CLI (command line interface) and CDAP UI.\n\n#### CDAP CLI\n\nIn order to deploy the directive through CLI, start the CDAP CLI and use the `load artifact` command to load the plugin artifact into CDAP. \n\n```\n$ $CDAP_HOME/bin/cdap cli\ncdap \u003e load artifact my-simple-udd-1.0-SNAPSHOT.jar config-file my-simple-udd-1.0-SNAPSHOT.json\n```\n\n#### CDAP UI\n![alt text](https://github.com/hydrator/example-directive/blob/develop/docs/directive-plugin.gif \"Logo Title Text 1\")\n\n## Example\n\nWe will now walk through the creation of a user defined directive(udd) called `text-reverse` that takes one argument: Column Name -- it is the name of the column in a `Row` that needs to be reversed. The resulting row will have the Column Name specified in the input have reversed string of characters.\n\n```\n !text-reverse :address\n !text-reverse :id\n```\n\nHere is the implementation of the above UDD. \n\n```\n@Plugin(type = UDD.Type)\n@Name(TextReverse.NAME)\n@Categories(categories = {\"text-manipulation\"})\n@Description(\"Reverses the column value\")\npublic final class TextReverse implements UDD {\n  public static final String NAME = \"text-reverse\";\n  private String column;\n  \n  public UsageDefinition define() {\n    UsageDefinition.Builder builder = UsageDefinition.builder(NAME);\n    builder.define(\"column\", TokenType.COLUMN_NAME);\n    return builder.build();\n  }\n  \n  public void initialize(Arguments args) throws DirectiveParseException {\n    this.column = ((ColumnName) args.value(\"column\").value();\n  }\n  \n  public List\u003cRow\u003e execute(List\u003cRow\u003e rows, RecipeContext context) throws RecipeException, ErrorRowException {\n    for(Row row : rows) {\n      int idx = row.find(column);\n      if (idx != -1) {\n        Object object = row.getValue(idx);\n        if (object instanceof String) {\n          String value = (String) object;\n          row.setValue(idx, new StringBuffer(value).reverse().toString());\n        }\n      }\n    }\n    return rows;\n  }\n}\n```\n\n### Code Walk Through\n\n#### Annontations\n\nFollowing annotations are required for the plugin. If any of these are missing, the plugin or the directive will not be loaded. \n\n* `@Plugin` defines the type of plugin it is. For all UDDs it's set to `UDD.Type`.\n* `@Name` defines the name of the plugin and as well as the directive name. \n* `@Description` provides a short description for the plugin and as well as for the directive. \n\n#### Call Pattern\n\nThe call pattern of UDD is the following :\n\n* **DEFINE** : During configure time either in the CDAP Pipeline Transform or Data Prep Service, the `define()` method is invoked only once to retrieve the information of the usage. The usage defines the specification of the arguments that this directive is going to accept. In our example of `text-reverse`, the directive accepts only one argument and that is of type `TokenType.COLUMN_NAME`.\n* **INITIALIZE** : During the initialization just before pumping in `Row`s through the directive, the `initialize()` method is invoked. This method is passed the arguments that are parsed by the system. It also provides the apportunity for the UDD writer to validate and throw exception if the value is not as expected.\n* **EXECUTE** : Once the pipeline has been setup, the `Row` is passed into the `execute()` method to transform. \n\n### Testing\n\nHere is the JUnit class that couldn't be any simpler. \n\n```\n  @Test\n  public void testBasicReverse() throws Exception {\n    TestRecipe recipe = new TestRecipe();\n    recipe.add(\"parse-as-csv :body ',';\");\n    recipe.add(\"set-headers :a,:b,:c;\");\n    recipe.add(\"text-reverse :b\");\n\n    TestRows rows = new TestRows();\n    rows.add(new Row(\"body\", \"root,joltie,mars avenue\"));\n    rows.add(new Row(\"body\", \"joltie,root,venus blvd\"));\n\n    RecipePipeline pipeline = TestingRig.pipeline(TextReverse.class, recipe);\n    List\u003cRow\u003e actual = pipeline.execute(rows.toList());\n\n    Assert.assertEquals(2, actual.size());\n    Assert.assertEquals(\"eitloj\", actual.get(0).getValue(\"b\"));\n    Assert.assertEquals(\"toor\", actual.get(1).getValue(\"b\"));\n  }\n```\n\n## That's it! Happy UDD'ing.\n\n \n  \n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdata-integrations%2Fimage-directives","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdata-integrations%2Fimage-directives","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdata-integrations%2Fimage-directives/lists"}