Skip to content

Instantly share code, notes, and snippets.

@Jamesits
Last active February 13, 2026 13:05
Show Gist options
  • Select an option

  • Save Jamesits/46862ff152b2c0e1414af66226632347 to your computer and use it in GitHub Desktop.

Select an option

Save Jamesits/46862ff152b2c0e1414af66226632347 to your computer and use it in GitHub Desktop.
Convert a text file to a patch file
#!/usr/bin/env bash
# convert a text file to a patch file
set -Eeuo pipefail
# usage: file2patch "source/file" "relative/destination/path" "patch/file"
file2patch() {
local SRCFILE="$1"
local PATCHPATH="$2"
local OUTFILE="$3"
local FILEMODE="$(stat -c '%a' "${SRCFILE}")"
local LINECOUNT="$(wc -l "${SRCFILE}" | cut -d' ' -f1)"
(
printf "diff --git a/%s b/%s\n" "${PATCHPATH}" "${PATCHPATH}"
printf "new file mode 100%s\n" "${FILEMODE}"
printf "index 00000000..11451419\n"
printf "%s\n" "--- /dev/null" # "printf --" will cause parsing problems
printf "+++ b/%s\n" "${PATCHPATH}"
printf "@@ -0,0 +1,%d @@\n" "${LINECOUNT}"
cat "${SRCFILE}" | sed -e 's/^/+/g'
) > "${OUTFILE}"
}
file2patch "$@"
@Ayushban1994
Copy link

From cb27e6f176e8e288c684364c0055f16a88cbceb1 Mon Sep 17 00:00:00 2001
From: Claude noreply@anthropic.com
Date: Fri, 13 Feb 2026 12:17:32 +0000
Subject: [PATCH] feat: add get_article operation node

Create a new 3P node that retrieves a DevRev article by ID, returning
extended fields: title, body, status, author, dates, tags, and
applies_to_parts.

.../functions/get_article_handler/index.ts | 96 +++++++++++++++++++
fixtures/get_article_event.json | 25 +++++
manifest.yaml | 95 ++++++++++++++++++
package.json | 22 +++++
tsconfig.json | 17 ++++
5 files changed, 255 insertions(+)
create mode 100644 code/src/functions/get_article_handler/index.ts
create mode 100644 fixtures/get_article_event.json
create mode 100644 manifest.yaml
create mode 100644 package.json
create mode 100644 tsconfig.json

diff --git a/code/src/functions/get_article_handler/index.ts b/code/src/functions/get_article_handler/index.ts
new file mode 100644
index 0000000..345b069
--- /dev/null
+++ b/code/src/functions/get_article_handler/index.ts
@@ -0,0 +1,96 @@
+import {

  • FunctionInput,
  • ExecuteOperationInput,
  • OperationOutput,
  • Error_Type,
    +} from '@devrev/typescript-sdk/dist/snap-ins';
    +import { client } from '@devrev/typescript-sdk';

+export const run = async (events: FunctionInput[]) => {

  • const event = events[0];
  • const payload = event.payload as ExecuteOperationInput;
  • const inputData = payload.data as {
  • article_id: string;
  • };
  • if (!inputData.article_id) {
  • return OperationOutput.fromJSON({
  •  error: {
    
  •    message: 'article_id is required',
    
  •    type: Error_Type.InvalidRequest,
    
  •  },
    
  • });
  • }
  • const devrevClient = client.setupBeta({
  • endpoint: event.execution_metadata.devrev_endpoint,
  • token: event.context.secrets.service_account_token,
  • });
  • try {
  • const response = await devrevClient.articlesGet({
  •  id: inputData.article_id,
    
  • });
  • const article = response.data.article;
  • return OperationOutput.fromJSON({
  •  output: {
    
  •    values: [
    
  •      {
    
  •        article_id: article.id,
    
  •        title: article.title ?? '',
    
  •        body: article.body ?? '',
    
  •        status: article.status ?? '',
    
  •        author_id: article.authored_by?.id ?? '',
    
  •        created_date: article.created_date ?? '',
    
  •        modified_date: article.modified_date ?? '',
    
  •        tags: (article.tags ?? []).map((tag: { name: string }) => tag.name),
    
  •        applies_to_parts: (article.applies_to_parts ?? []).map(
    
  •          (part: { id: string }) => part.id
    
  •        ),
    
  •      },
    
  •    ],
    
  •  },
    
  • });
  • } catch (error: any) {
  • const message = error?.response?.data?.message ?? error.message;
  • const statusCode = error?.response?.status;
  • if (statusCode === 404) {
  •  return OperationOutput.fromJSON({
    
  •    error: {
    
  •      message: `Article not found: ${inputData.article_id}`,
    
  •      type: Error_Type.NotFound,
    
  •    },
    
  •  });
    
  • }
  • if (statusCode === 401) {
  •  return OperationOutput.fromJSON({
    
  •    error: {
    
  •      message: 'Authentication failed',
    
  •      type: Error_Type.Unauthenticated,
    
  •    },
    
  •  });
    
  • }
  • if (statusCode === 403) {
  •  return OperationOutput.fromJSON({
    
  •    error: {
    
  •      message: 'Not authorized to access this article',
    
  •      type: Error_Type.Forbidden,
    
  •    },
    
  •  });
    
  • }
  • return OperationOutput.fromJSON({
  •  error: {
    
  •    message: `Failed to fetch article: ${message}`,
    
  •    type: Error_Type.Internal,
    
  •  },
    
  • });
  • }
    +};

+export default run;
diff --git a/fixtures/get_article_event.json b/fixtures/get_article_event.json
new file mode 100644
index 0000000..6f98a39
--- /dev/null
+++ b/fixtures/get_article_event.json
@@ -0,0 +1,25 @@
+{

  • "execution_metadata": {
  • "devrev_endpoint": "https://api.devrev.ai",
  • "event_type": "execute_operation"
  • },
  • "context": {
  • "dev_org_id": "DEV-123",
  • "snap_in_id": "snap_in_123",
  • "secrets": {
  •  "service_account_token": "test_token"
    
  • }
  • },
  • "payload": {
  • "metadata": {
  •  "slug": "get_article",
    
  •  "namespace": "get_article_node"
    
  • },
  • "data": {
  •  "article_id": "ARTICLE-123"
    
  • }
  • },
  • "input_data": {
  • "resources": {}
  • }
    +}
    diff --git a/manifest.yaml b/manifest.yaml
    new file mode 100644
    index 0000000..006e704
    --- /dev/null
    +++ b/manifest.yaml
    @@ -0,0 +1,95 @@
    +version: "2"
    +name: "Get Article"
    +description: "Retrieve a DevRev article by its ID"

+service_account:

  • display_name: "Get Article Bot"

+functions:

    • name: get_article_handler
  • description: "Handles fetching an article by ID"

+operations:

    • name: get_article
  • display_name: "Get Article"
  • description: "Fetch a DevRev article by its ID and return its details"
  • slug: get_article
  • function: get_article_handler
  • type: action
  • inputs:
  •  fields:
    
  •    - name: article_id
    
  •      field_type: id
    
  •      id_type:
    
  •        - article
    
  •      is_required: true
    
  •      description: "The ID of the article to retrieve"
    
  •      ui:
    
  •        display_name: "Article ID"
    
  •        placeholder: "ARTICLE-123"
    
  • outputs:
  •  fields:
    
  •    - name: article_id
    
  •      field_type: id
    
  •      id_type:
    
  •        - article
    
  •      description: "The ID of the article"
    
  •      ui:
    
  •        display_name: "Article ID"
    
  •    - name: title
    
  •      field_type: text
    
  •      description: "The title of the article"
    
  •      ui:
    
  •        display_name: "Title"
    
  •    - name: body
    
  •      field_type: rich_text
    
  •      description: "The body content of the article"
    
  •      ui:
    
  •        display_name: "Body"
    
  •    - name: status
    
  •      field_type: text
    
  •      description: "The current status of the article"
    
  •      ui:
    
  •        display_name: "Status"
    
  •    - name: author_id
    
  •      field_type: id
    
  •      id_type:
    
  •        - devu
    
  •      description: "The author of the article"
    
  •      ui:
    
  •        display_name: "Author"
    
  •    - name: created_date
    
  •      field_type: text
    
  •      description: "The date the article was created"
    
  •      ui:
    
  •        display_name: "Created Date"
    
  •    - name: modified_date
    
  •      field_type: text
    
  •      description: "The date the article was last modified"
    
  •      ui:
    
  •        display_name: "Modified Date"
    
  •    - name: tags
    
  •      field_type: array
    
  •      base_type: text
    
  •      description: "Tags associated with the article"
    
  •      ui:
    
  •        display_name: "Tags"
    
  •    - name: applies_to_parts
    
  •      field_type: array
    
  •      base_type: id
    
  •      id_type:
    
  •        - product
    
  •        - part
    
  •      description: "Parts that this article applies to"
    
  •      ui:
    
  •        display_name: "Applies to Parts"
    

diff --git a/package.json b/package.json
new file mode 100644
index 0000000..c893a04
--- /dev/null
+++ b/package.json
@@ -0,0 +1,22 @@
+{

  • "name": "get-article-node",
  • "version": "1.0.0",
  • "description": "Get Article operation node for DevRev",
  • "main": "dist/index.js",
  • "scripts": {
  • "build": "tsc",
  • "test": "jest",
  • "lint": "eslint 'code/src/**/*.ts'"
  • },
  • "dependencies": {
  • "@devrev/typescript-sdk": "^1.0.0",
  • "axios": "^1.6.0"
  • },
  • "devDependencies": {
  • "@types/jest": "^29.5.0",
  • "@types/node": "^20.0.0",
  • "jest": "^29.7.0",
  • "ts-jest": "^29.1.0",
  • "typescript": "^5.3.0"
  • }
    +}
    diff --git a/tsconfig.json b/tsconfig.json
    new file mode 100644
    index 0000000..76b4eb1
    --- /dev/null
    +++ b/tsconfig.json
    @@ -0,0 +1,17 @@
    +{
  • "compilerOptions": {
  • "target": "ES2020",
  • "module": "commonjs",
  • "lib": ["ES2020"],
  • "outDir": "./dist",
  • "rootDir": "./code/src",
  • "strict": true,
  • "esModuleInterop": true,
  • "skipLibCheck": true,
  • "forceConsistentCasingInFileNames": true,
  • "resolveJsonModule": true,
  • "declaration": true
  • },
  • "include": ["code/src/**/*"],
  • "exclude": ["node_modules", "dist", "**/*.test.ts"]
    +}
    --
    2.43.0

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