diff --git a/.agents/skills/review-translation/SKILL.md b/.agents/skills/review-translation/SKILL.md new file mode 100644 index 000000000000..db0d76de6164 --- /dev/null +++ b/.agents/skills/review-translation/SKILL.md @@ -0,0 +1,116 @@ +--- +name: review-translation +description: + Checks translated docs pages against the English in a fresh session, fixes + clear problems (changed meaning, literal phrasing, broken house style rules), + and reports the rest. Does not commit or open a PR. Use after /translate + pages, once the section is committed. +disable-model-invocation: true +--- + +# Review a translation + +Run `/review-translation ` in a fresh session, not the one that +translated the pages: a translator is poor at spotting its own mistakes. Commit +the translation first, so the review's fixes show up as their own diff. The +scope is the same as for `/translate pages`: one page, one folder under `docs/`, +or one sidebar category. If no scope is given, ask. + +The review checks against the rules the translator followed: "How to translate" +in `.agents/skills/translate/SKILL.md`, the house style in +`.agents/skills/translate/.md`, the rules for the locale in +`translation-rules.yml`, and `glossary.yml`. + +The review fixes clear problems in `machine` pages and reports the rest. It does +not commit, open a PR, or add rules. Never edit a `human-reviewed` page or a +fenced block; report the problem instead. + +## 1. Lay out the pages + +Read each translated page next to its English page in `docs/`. The translation +keeps the same structure, so the blocks line up in order. + +Then read the rules listed above. + +## 2. Search for rule breaks + +These need no judgement, so search for them across the whole scope rather than +reading for them: + +- Run the check: + + ```bash + node .agents/skills/translate/check-translation.js docs/.md... + ``` + + Each block it lists is a candidate, not a verdict: read it against the + English. It skips rules with their context in brackets, like "upgrade (a + plan)", so search for those by hand. + +- Punctuation `.md` rules out, such as curly quotes. +- English terms kept from `glossary.yml` written with a capital mid-sentence, if + `.md` says to lower-case them. Case-sensitive terms (OpenFn, Canvas, + Inspector) and names of things in the app keep their capitals. + +## 3. Read each block against its English + +Check that the translation: + +- **Says the same thing.** Nothing added, dropped, or softened. Numbers, dates, + limits, and "must", "should", and "can" match, and so does who does what. +- **Reads naturally.** No word-for-word phrasing, repeated subjects, stacked + nouns, or asides that repeat the sentence. +- **Uses the right voice**, as `.md` sets it. In Spanish, watch for verb + forms that read as "usted". +- **Keeps names of things in the app** exactly as in the English, and keeps + links and inline code the same. +- **Is consistent across the scope.** The same English term gets the same + translation on every page. + +Do not flag wording you would only have written differently. If the English +itself is wrong or unclear, note it under "Problems in the English" and leave +the translation matching it. + +## 4. Fix what is clear + +Fix a block when the problem and the fix are both clear. Change only the words +that are wrong, so the diff shows just the fix. Leave the front matter alone. +Then run `yarn prettier --write ` on the pages you changed, and run the +check again. + +## 5. Report + +Build the site and serve it (`yarn serve`), so each item can point to the page +as the reader sees it. Report in the chat. Place each item by the page URL, such +as `http://localhost:3000/es/documentation/build/triggers`, and the section +heading, not by file and line. Group the items like this: + +- **Fixed.** For each fix, give the English sentence, the translation before, + and the translation after, all in full, with the changed words in bold. Then + say why in one line. +- **Needs a decision.** Problems you were not sure how to fix, or where the fix + changes meaning. Quote the English and the current translation. +- **Problems in the English.** +- **Suggested rules.** A problem found on more than one page, or likely to come + back in later sections, with the line you would add to `.md` or + `translation-rules.yml`. Add it only if asked. +- **Page to spot-check.** The page with the most fixes, with its URL. + +## Marking a page human-reviewed + +Only a person who reads the language well marks a page `human-reviewed`. It +means they checked the meaning against the English, not only that the +translation reads well: a page can read perfectly and still say something +different. With the translated page open next to the English one on the built +site, they check every paragraph for the points in step 3, fix what is wrong, +and then set in the front matter: + +```yaml +translation_review_status: human-reviewed +translation_reviewer: +translation_review_date: +``` + +A fix that would apply to other pages goes into `.md` or +`translation-rules.yml` too. A `human-reviewed` page is never retranslated in +full again; changes to its English come to the reviewer as a separate PR. diff --git a/.agents/skills/translate/SKILL.md b/.agents/skills/translate/SKILL.md index 5900ffca80d8..de56f512fd31 100644 --- a/.agents/skills/translate/SKILL.md +++ b/.agents/skills/translate/SKILL.md @@ -1,136 +1,76 @@ --- name: translate description: - Translates English docs pages into Spanish and French under i18n/, respecting - glossary.yml, translation-rules.yml, review status, and do-not-retranslate - fences, and opens one PR per locale. Use when asked to translate or refresh - translations. + Translates the docs under i18n/ into each language enabled in + docusaurus.config.js, either a set of English pages or the interface text + (navbar, footer, sidebar headings, homepage). Respects glossary.yml, + translation-rules.yml, each locale's house style, review status, and + do-not-retranslate fences, and opens one PR per locale. Use when asked to + translate or refresh translations. disable-model-invocation: true --- # Translate -Translate English docs into Spanish (`es`) and French (`fr`). The English is -always the source of truth. Translations are generated files that live in this -repo. Save each one at the same path as the English page, under -`i18n//docusaurus-plugin-content-docs/current/`. For example, -`docs/build/triggers.md` goes to -`i18n/es/docusaurus-plugin-content-docs/current/build/triggers.md`. Docusaurus -ignores a file anywhere else without an error, and the page stays English. +Translate English docs into each locale in `i18n.locales` in +`docusaurus.config.js`, other than English. The English is always the source of +truth. Translations are generated files that live in this repo, under +`i18n//`. -The sidebar headings come from `sidebars-main.js`, not from the pages. If it has -new or renamed entries, run -`yarn docusaurus write-translations --locale `. This adds them to -`i18n//docusaurus-plugin-content-docs/current.json` in English and keeps -the ones already translated. Translate the new ones. +There are two tasks. Each has its own file in this folder; read the one you +need. + +- **`/translate pages `** translates a set of pages. The scope is one + page, one folder under `docs/`, or one sidebar category. With no scope, it + covers every page that needs it. See `pages.md`. +- **`/translate interface`** translates the text that is not in a page: the + navbar, footer, sidebar headings, and homepage. Run it when + `sidebars-main.js`, the navbar or footer in `docusaurus.config.js`, or any + `` text in `src/` has changed. See `interface.md`. + +If you are not told which task, work out which ones are needed from what has +changed in English, say so, and ask before starting. Never translate the generated adaptor pages, the job library, the old v1 docs, or articles and blog posts. ## Before you start -Check these three things. If any fails, stop and ask. +Check these five things. If any fails, stop and ask. - The locale is enabled in `docusaurus.config.js`. Do not enable it yourself; that changes what gets deployed. - `i18n/` is not in `.gitignore`. - `glossary.yml` and `translation-rules.yml` are valid YAML. - -Translate the English page exactly as it is on disk, so the hash you record -matches what you translated. Do not reformat it; English changes belong in their -own PR. After writing the translation, run `yarn prettier --write ` on -only the files you changed under `i18n/`. - -## Front matter - -Copy the English page's front matter. Translate only `title` and -`sidebar_label`. Then add: - -```yaml -translation_source_hash: -translation_review_status: machine -``` - -The hash is the content hash of the English file, from -`git hash-object docs/.md`, not a commit. Commits do not survive squash -merges: a hash pointing at a commit made on a branch dangles as soon as the -branch is squashed onto main. A content hash is the same wherever the file -lives, and it answers the only question the field exists to answer: is the -English still the version this was translated from? To compare, hash the current -English file and check it against the recorded value. - -`translation_review_status` can be `machine`, `needs-review`, or -`human-reviewed`. Only a human ever sets `human-reviewed`, and when they do they -also add `translation_reviewer` and `translation_review_date`. - -## Decide what to do with each page - -- **No translation yet.** Translate the whole page. -- **The hash matches the current English file.** Skip it, whatever its status. - The English has not changed since it was translated. The one exception: if - `glossary.yml` or `translation-rules.yml` was committed more recently than the - translation (compare `git log -1 --format=%ct -- `), treat a `machine` - page as if the hash no longer matches, so it picks up the new rules. -- **The hash no longer matches, and the status is `machine`, `needs-review`, or - missing.** Translate the whole page again, but keep any fenced blocks (see - below) exactly as they were. -- **The hash no longer matches, and the status is `human-reviewed`.** Leave the - file out of the translation PR. Instead, open a separate PR for the named - reviewer that changes only the affected parts. Recover the English the - reviewer saw with `git cat-file -p `, diff it against the - current English, and translate only what changed. In the same PR, set - `translation_source_hash` to the current English hash and leave the status as - `human-reviewed`: the reviewer merging it approves it. If the old version is - no longer in the repo, say so and offer a full retranslation in that PR - instead. - -## Fenced blocks - -A human can wrap part of a translation like this: - -```markdown - -Text a reviewer has corrected by hand. - -``` - -Copy those blocks into the new translation exactly, in the same place. If the -English they correspond to has been deleted, keep the block anyway and ask what -to do with it. +- The locale has a house style guide, `.md`, in this folder. +- Your branch has everything on `main`. Run `git fetch origin main` and then + `git merge-base --is-ancestor origin/main HEAD`. If it fails, the English you + would translate is out of date, and the hashes you record will not match + `main`. Ask to merge `main` in first. If your branch comes off another branch, + merge `main` into that one, then that one into yours. ## How to translate -- Words in `glossary.yml` stay in English. For ordinary words that are also - product terms, like "run" or "step", keep the English only when the word means - the OpenFn thing. -- Follow any rules for the locale in `translation-rules.yml`. By default, - Spanish uses "tú" and French uses "vous". -- Copy code blocks and inline code exactly. You may translate comments inside - code. +These apply to both tasks. + +- Terms in `glossary.yml` stay in English, unless the term lists a word for the + locale under `locales`, such as `es: proyecto`. For ordinary words that are + also product terms, like "run" or "step", keep the English only when the word + means the OpenFn thing. +- Follow the house style for the locale in `.md` in this folder, such as + `es.md`, and any rules for the locale in `translation-rules.yml`. +- Write the way a native writer would, not word for word. Reorder or split a + sentence when the literal version is awkward, drop a subject the sentence has + already given, and cut an aside that repeats what the sentence says. Keep the + meaning and the facts; change only the wording. +- Copy code blocks and inline code exactly, comments included. - Keep the names of things in the app, like buttons, menus, tabs, and field labels, exactly as they are in the English. The app is English only, so a translated button name points the reader at a button that does not exist. -- Keep the same structure: same headings at the same levels, same lists, same - callouts, same components. -- Keep links exactly as they are in the English. Do not add `/es/` or `/fr/`; - Docusaurus adds the locale when it builds the page. If the English has a - relative link like `../deploy/portability.md`, it breaks the translated build, - so fix it in the English first (see `STYLE.md`). -- Give translated headings the original English anchor so existing links still - work. - -## Before you commit - -Check that the fixed glossary terms (the ones without `product_noun: true`, such -as OpenFn, Lightning, adaptor, webhook) appear as many times as in the English. -Product nouns like "run" and "step" are allowed to differ, since their -ordinary-English uses get translated. Before counting, join each file into one -line with single spaces: Prettier wraps prose at 80 columns, and English and -Spanish wrap at different points, so a multi-word term like "work order" can sit -across a line break in one file and not the other. Check the code blocks are -identical. Check the counts of headings, code blocks, callouts, images, and -tables match. Check the front matter is complete. Check every fenced block -survived. Then build the site and make sure it passes: + +## Before you open the PR + +Build the site and make sure it passes: ```bash yarn generate-library @@ -144,6 +84,7 @@ broken. Open one PR per locale, separate from the English PR. Translated files do not count toward the 20-file limit, because a locale's translations are reviewed as -a set. In the PR description, say which tool and model translated the pages. If -you spot a problem in the English while translating, note it for the next -English pass; do not fix it here. +a set. In the PR description, say which tool and model translated the text. If +you spot a problem in the English while translating, list it in the PR +description under "Problems in the English", with the file and line. Do not fix +it in this PR. diff --git a/.agents/skills/translate/check-translation.js b/.agents/skills/translate/check-translation.js new file mode 100644 index 000000000000..257668759175 --- /dev/null +++ b/.agents/skills/translate/check-translation.js @@ -0,0 +1,230 @@ +#!/usr/bin/env node +// Checks translated pages against the English they were translated from. +// +// node .agents/skills/translate/check-translation.js es docs/get-started/*.md +// +// Lists blocks where a fixed glossary term appears fewer times in the +// translation than in the English, which can mean it was translated. It also +// lists blocks that may break a rule for the locale in translation-rules.yml, +// or use a word from the "Not" column in .md. +// +// A block is a run of lines between blank lines, after formatting with +// Prettier, and a fenced code block is one block. Both sides go through +// Prettier so wrapping differences don't count as changes. +const fs = require('fs'); +const { execFileSync } = require('child_process'); +const prettier = require('prettier'); +const yaml = require('js-yaml'); // installed with Docusaurus + +const FENCE_MARKERS = [ + '', + '', +]; + +// Terms that must stay in English, unless `locales` gives the locale a word. +// Product nouns like "run" are left out, since their ordinary-English uses get +// translated. +const FIXED = yaml + .load(fs.readFileSync('glossary.yml', 'utf8')) + .terms.filter(t => !t.translate && !t.product_noun) + .map(t => ({ + term: t.term, + locales: t.locales || {}, + re: new RegExp( + `\\b${t.term.replace(/ /g, '\\s+')}s?\\b`, + t.case_sensitive ? 'g' : 'gi' + ), + })); + +// Heading anchors and link targets are copied unchanged, so count only prose. +const prose = s => s.replace(/\{#[^}]*\}/g, '').replace(/\]\([^)]*\)/g, ']'); + +// A higher count in the translation is harmless; a lower one is worth a look. +const termDrops = (locale, english, translation) => + FIXED.filter(t => !t.locales[locale]) + .map(({ term, re }) => ({ + term, + en: (prose(english).match(re) || []).length, + es: (prose(translation).match(re) || []).length, + })) + .filter(c => c.es < c.en) + .map(c => `"${c.term}" ${c.en} in English, ${c.es} in translation`); + +// Phrase matching for the locale's rules. Accents and case are ignored, and +// inline code is skipped. "a / b" matches either. A word may take a plural, so +// "feature" finds "features" and "función" finds "funciones" but not +// "funcionalidad". An English phrase also matches its -d, -ed, and -ing forms, +// so "enable" finds "enabled" and "enabling". A one-word Spanish verb matches +// its conjugations, so "mejorar" finds "mejora" and "mejorado" but not "mejor". +const fold = s => + s.normalize('NFD').replace(/\p{M}/gu, '').toLowerCase().replace(/\s+/g, ' '); +const has = (text, phrase, english = false) => { + const t = fold(prose(text).replace(/`[^`]*`/g, '')); + return phrase.split('/').some(alt => { + const p = fold(alt.replace(/\s*\(.*?\)/g, '').trim()); + const verb = !english && !p.includes(' ') && /(ar|er|ir)$/.test(p); + const stem = english + ? p.replace(/e$/, '') + : verb + ? p.replace(/(ar|er|ir)$/, '') + : p; + const end = english + ? '(?:e|es|s|ed|d|ing)?(?!\\p{L})' + : verb + ? '[aeio]\\p{L}*' + : '(?:e?s)?(?!\\p{L})'; + const escaped = stem.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + return new RegExp(`(?.md. Each returns the problems in one block. A rule +// whose source names its context, like "upgrade (a plan)", is left out: only a +// reader can tell which uses it covers. So is a rule missing source or target. +function rulesFor(locale) { + const rules = ( + yaml.load(fs.readFileSync('translation-rules.yml', 'utf8')).rules || [] + ).filter( + r => + (r.locale === locale || r.locale === '*') && + r.source && + r.target && + !r.source.includes('(') + ); + const styleFile = `.agents/skills/translate/${locale}.md`; + const banned = fs.existsSync(styleFile) + ? fs + .readFileSync(styleFile, 'utf8') + .split('\n') + .map(l => l.match(/^\|([^|]+)\|([^|]+)\|$/)) + .filter(m => m && !/^[\s-]+$/.test(m[2]) && m[2].trim() !== 'Not') + .map(m => ({ use: m[1].trim(), not: m[2].trim() })) + : []; + return (english, translation) => [ + ...rules + .filter(r => + r.kind === 'avoid' + ? has(english, r.source, true) && has(translation, r.target) + : has(english, r.source, true) && !has(translation, r.target) + ) + .map(r => `rule "${r.source}": ${r.instruction.replace(/\s+/g, ' ')}`), + ...banned + .filter(b => has(translation, b.not)) + .map(b => `${locale}.md: use "${b.use}", not "${b.not}"`), + ]; +} + +function splitFrontMatter(text) { + const m = text.match(/^---\n([\s\S]*?)\n---\n/); + return m + ? { fm: m[1], body: text.slice(m[0].length) } + : { fm: '', body: text }; +} + +// do-not-retranslate markers are dropped so the blocks line up. +async function blocks(text, filepath) { + const options = { + ...(await prettier.resolveConfig(filepath)), + parser: 'markdown', + }; + const formatted = await prettier.format(splitFrontMatter(text).body, options); + const out = []; + let cur = []; + let inCode = false; + const flush = () => { + if (cur.length) out.push(cur.join('\n')); + cur = []; + }; + for (const line of formatted.split('\n')) { + if (!inCode && FENCE_MARKERS.includes(line.trim())) { + flush(); + continue; + } + if (/^\s*(```|~~~)/.test(line)) inCode = !inCode; + if (!inCode && line.trim() === '') flush(); + else cur.push(line); + } + flush(); + return out; +} + +async function page(locale, enPath) { + const rel = enPath.replace(/^docs\//, ''); + const esPath = `i18n/${locale}/docusaurus-plugin-content-docs/current/${rel}`; + if (!fs.existsSync(esPath)) + return { page: rel, error: 'No translation yet.' }; + + // Check against the English the page was translated from. + const esText = fs.readFileSync(esPath, 'utf8'); + const hash = (splitFrontMatter(esText).fm.match( + /^translation_source_hash: *(.*)$/m + ) || [])[1]; + if (!hash) + return { + page: rel, + error: 'No translation_source_hash in the front matter.', + }; + let enBlocks; + try { + const english = execFileSync('git', ['cat-file', '-p', hash], { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], + }); + enBlocks = await blocks(english, enPath); + } catch { + return { + page: rel, + error: `The English it was translated from (${hash}) is not in the repo.`, + }; + } + const esBlocks = await blocks(esText, esPath); + + // Compare block by block where the blocks line up, otherwise the whole page. + const pairs = + enBlocks.length === esBlocks.length + ? enBlocks.map((english, i) => ({ english, translation: esBlocks[i] })) + : [ + { + english: enBlocks.join('\n\n'), + translation: esBlocks.join('\n\n'), + }, + ]; + const ruleBreaks = rulesFor(locale); + return { + page: rel, + problems: pairs + .map(r => ({ + ...r, + found: [ + ...termDrops(locale, r.english, r.translation), + ...ruleBreaks(r.english, r.translation), + ], + })) + .filter(r => r.found.length), + }; +} + +async function main() { + const args = process.argv.slice(2); + const [locale, ...files] = args; + if (!locale || !files.length) { + console.error('Usage: check-translation.js ...'); + process.exit(1); + } + const flat = s => s.replace(/\s+/g, ' '); + let found = 0; + for (const f of files) { + const p = await page(locale, f); + if (p.error) console.log(`${p.page}: ${p.error}`); + for (const r of p.problems || []) { + found++; + console.log( + `${p.page}: ${r.found.join('; ')}\n en: ${flat(r.english)}\n ${locale}: ${flat(r.translation)}\n` + ); + } + } + if (!found) console.log('No glossary terms missing and no rules broken.'); +} + +main(); diff --git a/.agents/skills/translate/es.md b/.agents/skills/translate/es.md new file mode 100644 index 000000000000..5e9cb8cb3641 --- /dev/null +++ b/.agents/skills/translate/es.md @@ -0,0 +1,74 @@ +# Spanish house style + +Humans maintain this file. Read it before translating into Spanish (`es`). + +## Variety and voice + +Write neutral Latin American Spanish (es-419), for readers across the region. +Avoid slang from any one country. + +- Address the reader as "tú", never "usted", "vosotros", or "vos". A verb with + no subject can read as "usted" ("podría crear"), so use the "tú" form + ("podrías crear"). +- When an English example switches to "I" or "we", keep it addressed to the + reader. +- Keep how strong the English is. "Must" is "debe" or "tienes que", "should" is + "debería" or "deberías", and "can" is "puede" or "puedes". Do not write "debe" + for "should". + +| Use | Not | +| ---------------------------------- | --------------------- | +| computadora | ordenador | +| capacitación (training) | formación | +| lista de verificación (checklist) | lista de comprobación | +| contactar a | contactar con | +| reportar (a problem) | informar de | +| recolección de datos | recogida de datos | +| confiable | fiable | +| actualmente (currently) | ahora mismo | +| considerar | plantearse | +| por ejemplo, or como before a list | p. ej. | +| en campo (in the field) | en terreno | +| intentar (try to) | probar a | +| entrada (input) | input | + +Keep `Input` when it names the panel in the app. "Payload" stays in English. + +## English terms kept in Spanish + +Terms from `glossary.yml` that stay in English take a fixed gender and an +English plural with "-s". + +- Feminine: la work order, la CLI, la API, la iPaaS, la pull request. +- Masculine: el workflow, el step, el job, el run, el trigger, el adaptor, el + webhook, el dataclip, el sandbox, el Canvas, el Inspector, el cron. +- Any English term not listed is masculine. + +For example, "The work order creates two runs" becomes "La work order crea dos +runs". + +Before a kept English term, choose "y" or "e", and "o" or "u", by how the +English word sounds: "workflows y History", "Canvas e Inspector". + +In a section that defines terms, you may explain a glossary term once in +brackets, with the English first: "Workflow (flujo de trabajo)". Everywhere +else, use the English term on its own. + +## Capitals + +Follow Spanish capitalization, not the English. Spanish capitalizes far less, +and copying the English makes the page read as a translation. + +- In titles and headings, capitalize only the first word and proper names. +- Capitalize proper names: product and organization names, named programs and + publications, case-sensitive glossary terms (OpenFn, Lightning, Canvas, + Inspector, CLI), and labels copied from the app. +- Write everything else in lower case, including kept English terms mid-sentence + and categories the English capitalizes. "A Project contains Workflows, + Triggers, and Credentials" becomes "Un proyecto contiene workflows, triggers y + credenciales". "Digital Public Good" becomes "bien público digital". + +## Punctuation + +- Use straight double quotes ("...") for every quotation, even where the English + uses curly quotes. Do not use curly quotes or comillas angulares («...»). diff --git a/.agents/skills/translate/interface.md b/.agents/skills/translate/interface.md new file mode 100644 index 000000000000..92ae7726bc29 --- /dev/null +++ b/.agents/skills/translate/interface.md @@ -0,0 +1,38 @@ +# Translate the interface text + +Read `SKILL.md` first. It has the checks to run before you start, the +translation rules, and how to build and open the PR. + +The navbar, footer, sidebar headings, and homepage are not pages. Their text +lives in JSON files under `i18n//`. Create or update them with: + +```bash +yarn docusaurus write-translations --locale +``` + +This adds new entries in English and keeps the ones already translated. +Translate the `message` value of each new entry. Leave the keys and +`description` values alone. + +It writes more than we want. Keep only these: + +| File | Keep | +| ---------------------------------------------- | -------------------------------------------------------------------------------------------- | +| `code.json` | Everything except the `theme.*` entries | +| `docusaurus-theme-classic/navbar.json` | Everything. Leave `title` and `logo.alt` as `OpenFn` | +| `docusaurus-theme-classic/footer.json` | Everything except `copyright` | +| `docusaurus-plugin-content-docs/current.json` | Everything. These are the sidebar headings. Leave `version.label` as it is | +| `docusaurus-plugin-content-blog*/options.json` | Everything. These are the titles and sidebar heading of the blog and articles, not the posts | + +Remove the rest before you commit: + +- The `theme.*` entries in `code.json`. Docusaurus already ships them + translated, and a copy here would override theirs and go stale when Docusaurus + is upgraded. +- The `copyright` entry in `footer.json`. The site works out the year when it + builds, and a translated copy would freeze it. +- Every other file: the adaptor sidebar + (`docusaurus-plugin-content-docs-adaptors/`) and the old v1 docs + (`version-legacy.json`). Those stay in English. + +Then build and open the PR as `SKILL.md` describes. diff --git a/.agents/skills/translate/pages.md b/.agents/skills/translate/pages.md new file mode 100644 index 000000000000..23f3cbb057fc --- /dev/null +++ b/.agents/skills/translate/pages.md @@ -0,0 +1,157 @@ +# Translate pages + +Read `SKILL.md` first. It has the checks to run before you start, the +translation rules, and how to build and open the PR. + +Save each translation at the same path as the English page, under +`i18n//docusaurus-plugin-content-docs/current/`. For example, +`docs/build/triggers.md` goes to +`i18n/es/docusaurus-plugin-content-docs/current/build/triggers.md`. Docusaurus +silently ignores a file anywhere else. + +Translate the English page exactly as it is on disk, so the hash you record +matches what you translated. Do not reformat it; English changes belong in their +own PR. After writing the translation, run `yarn prettier --write ` on +only the files you changed under `i18n/`. + +## Front matter + +Copy the English page's front matter. Translate only `title` and +`sidebar_label`. Then add: + +```yaml +translation_source_hash: .md> +translation_review_status: machine +``` + +The hash is the English file's content hash, not a commit, because commits do +not survive squash merges. + +`translation_review_status` is `machine` or `human-reviewed`. Only a human sets +`human-reviewed`, and adds `translation_reviewer` and `translation_review_date` +with it. + +## Decide what to do with each page + +- **No translation yet.** Translate the whole page. To retranslate a page from + scratch on purpose, delete it first. +- **The hash matches the current English.** Skip it, unless the page is + `machine` and this lists any commits: + + ```bash + git log --oneline $(git log -1 --format=%H -- )..HEAD -- glossary.yml translation-rules.yml .agents/skills/translate/.md + ``` + + Then see "When the rules change". + +- **The hash does not match, and the status is `machine` or missing.** See + "Updating a page". +- **The hash does not match, and the status is `human-reviewed`.** Leave it out + of the translation PR. Open a separate PR for the named reviewer: diff the + English they saw against the current English as in "Updating a page", and + translate only what changed. Set the new hash and leave the status as + `human-reviewed`; the reviewer merging it approves it. If the old English is + no longer in the repo, say so and offer a full retranslation. +- **The English page has moved or been deleted.** Move the translation to match + with `git mv`, or delete it. Docusaurus silently ignores a translation with no + English page. + +## When the rules change + +Do not retranslate the page; a reviewer could no longer see what the rules +changed. + +1. See what changed in the rules since the page was last committed: + + ```bash + git diff $(git log -1 --format=%H -- ) -- glossary.yml translation-rules.yml .agents/skills/translate/.md + ``` + +2. Fix only the text that breaks a new or changed rule. Leave everything else, + even wording you would now write differently. + +If nothing breaks the new rules, there is nothing to commit. + +## Updating a page + +Change only what the English changed, so the reviewer sees only that. Diff the +English the page was translated from against the current English: + +```bash +git diff --word-diff HEAD:docs/.md +``` + +Edit the existing translation in place, block by block: + +- If only links, inline code, or heading anchors changed, copy those changes + into the translation. +- If the translation already says what the new English says, keep it. A typo fix + often needs nothing. +- Otherwise, translate the changed text, reusing the old wording where it still + fits. + +If the recorded English is not in the repo (`git diff` fails), translate the +whole page again, keeping fenced blocks, and say so in the PR. + +Update `translation_source_hash` even if no translated text changed. In the PR +description, list for each page how many blocks changed and how many needed new +text. + +## Fenced blocks + +A human can wrap a corrected part of a translation like this: + +```markdown + + +Text a reviewer has corrected by hand. + +``` + +Leave fenced blocks exactly as they are, in the same place. If the English a +fenced block belongs to has changed or gone, keep the block and ask what to do +with it. If a fenced block breaks a rule, say so in the PR. + +## Page rules + +These are on top of the translation rules in `SKILL.md`. + +- Keep the same structure: same headings at the same levels, same lists, same + callouts, same components. +- Keep links exactly as they are in the English. Do not add `/es/`; Docusaurus + adds it. A relative link like `../deploy/portability.md` breaks the translated + build, so fix it in the English first (see `STYLE.md`). +- Give each translated heading its English anchor, as `{#anchor}` after the + heading, so existing links still work. To get the anchors, let Docusaurus + write them into a copy of the English page: + + ```bash + cp docs/.md /page.md + yarn docusaurus write-heading-ids . /page.md + ``` + + It keeps the underscores from `_italic_` text in the anchor, which the site + does not: `See _Now_` gives `see-_now_`, but the site uses `see-now`. Drop + them. + +## Check each page + +```bash +node .agents/skills/translate/check-translation.js docs/.md... +``` + +This lists blocks where a fixed glossary term appears fewer times in the +translation than in the English, or that may break a rule in +`translation-rules.yml` or `.md`. Each is a candidate. If a term was +translated, put the English back. If Spanish just uses it fewer times, for +example by dropping a repeated subject, leave it. + +Then check by hand that: + +- code blocks are identical to the English +- the counts of headings, code blocks, callouts, images, and tables match +- the front matter is complete +- every fenced block survived + +Then build as `SKILL.md` describes, commit, and run `/review-translation` in a +fresh session. diff --git a/AGENTS.md b/AGENTS.md index e6206cf9f6f5..36ee27b559c0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,15 +25,15 @@ you need. **Ask before editing** -- `docusaurus.config.js`, `package.json`, and anything in `.github/`. These - change how the site builds and deploys. +- `docusaurus.config.js`, `package.json`, `sidebars-adaptors.js`, and anything + in `.github/`. These change how the site builds and deploys. **Special rules apply** - Translations in `i18n/`. See `translate/SKILL.md`. -- The two rule files: `glossary.yml` and `translation-rules.yml`. Humans - maintain these. Each explains its format at the top. Only add an entry if the - user asks you to. +- The rule files: `glossary.yml`, `translation-rules.yml`, and the house style + for each locale in `.agents/skills/translate/.md`. Humans maintain + these. Only add an entry if the user asks you to. To check facts, you can read the product code. Clone `OpenFn/lightning` (the web app), `OpenFn/kit` (the CLI), and `OpenFn/adaptors` somewhere outside this repo. @@ -48,7 +48,11 @@ Never change them. or from an identify-gaps report. - **`release-review`** works out what the product shipped recently and passes that to identify-gaps. Suited to a monthly schedule. -- **`translate`** translates English pages, in its own PR per locale. +- **`translate`** translates English pages (`/translate pages`) or the interface + text (`/translate interface`), in its own PR per locale. +- **`review-translation`** checks translated pages against the English, fixes + clear problems, and reports the rest. Commit the translation, then run it in a + fresh session. `update-content` and `translate` open PRs, so they only run when someone asks for them by name (`/update-content`, `/translate`). When another skill hands off @@ -70,7 +74,8 @@ category from the sidebar, one folder under `docs/`, or one page. - Never edit generated adaptor pages. Draft an issue for `OpenFn/adaptors` and put it in the PR. Only file it if asked. - Never retranslate text inside `` fences. -- Never translate a term listed in `glossary.yml`. +- Never translate a term listed in `glossary.yml`, unless the term lists a word + for the locale under `locales`. - Never retake, crop, or replace screenshots. - Never disable a check to make the build pass. @@ -93,6 +98,10 @@ yarn generate-adaptors yarn build ``` +`yarn build` builds every locale, so an English change can break a translated +page, for example by moving a page it links to. Don't add `--locale` to speed it +up. + Broken-anchor warnings do not fail the build, and `main` already has some. Fix only the ones your change adds. diff --git a/README.md b/README.md index c7b173db89d9..be5e93e4198b 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,16 @@ yarn start-offline This command skips the adaptor docs step, which requires an active internet connection. +### Start in another language + +``` +yarn start --locale es +``` + +The development server shows one language at a time. This serves the Spanish +site at the root, with English wherever a page isn't translated yet. To see +both languages together at `/` and `/es/`, build and serve the site as below. + ### Building the job library ``` diff --git a/docs/build-for-developers/cli-usage.md b/docs/build-for-developers/cli-usage.md index d3acc62a5455..0cf56f75d2c1 100644 --- a/docs/build-for-developers/cli-usage.md +++ b/docs/build-for-developers/cli-usage.md @@ -234,7 +234,7 @@ So you want to write unit tests against your job code? See the full guide. Unit tests only work against pure functions in your code (top level function -declarations): they do not qork against operations or adaptor functions because +declarations): they do not work against operations or adaptor functions because they require connected backend services. To unit test functions in your job code: @@ -253,8 +253,8 @@ openfn compile --exports-only Compiled files are written to `dist/` as `.mjs` files. -With `--exports-only` Operations are stripped out entirely, leaving only -exported functions and variables . +With `--exports-only`, operations are stripped out entirely, leaving only +exported functions and variables. **Compile a single workflow by name:** @@ -262,7 +262,7 @@ exported functions and variables . openfn compile my-workflow --exports-only ``` -**Recompile whenever a job code changes:** +**Recompile whenever job code changes:** ```bash openfn compile --exports-only --watch diff --git a/docs/get-started/terminology.md b/docs/get-started/terminology.md index 50c27801b676..2885a84bfad0 100644 --- a/docs/get-started/terminology.md +++ b/docs/get-started/terminology.md @@ -242,8 +242,8 @@ The Inspector has 3 key interfaces: `Input`, `Editor`, & `Output`. ### Input An Input is the data (`json`) that is used as the starting Input for a Workflow -Step to utilise when it's run. Every Run will have an Input (initial state) and -Output (final state). +Step to utilise when it's run. Every Run has an Input (the initial `state`) and +an Output (the final `state`). Inputs may be created automatically by a webhook event (e.g., a message forwarded or JSON payload posted to OpenFn) or another Workflow Step, or diff --git a/docs/jobs/best-practices.md b/docs/jobs/best-practices.md index e26dc1e3327d..db347090195e 100644 --- a/docs/jobs/best-practices.md +++ b/docs/jobs/best-practices.md @@ -102,5 +102,5 @@ fn(state => ({ })); ``` -To test the export helpers -[See Unit testing jobs](/documentation/jobs/unit-testing-jobs) +To test the exported helpers, see +[Unit testing jobs](/documentation/jobs/unit-testing-jobs). diff --git a/docs/jobs/unit-testing-jobs.md b/docs/jobs/unit-testing-jobs.md index cef30767d666..10c76a4fa251 100644 --- a/docs/jobs/unit-testing-jobs.md +++ b/docs/jobs/unit-testing-jobs.md @@ -8,7 +8,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; Most job code goes like this: fetch some records, reshape them, send them somewhere else. But the reshaping bit often grows into complex logic - parsing a string into a structured record, mapping local codes onto DHIS2 data elements, -normalising a dozen date formats into one. +normalizing a dozen date formats into one. Unit testing that logic helps to validate that the code runs correctly, and helps to prevent errors occurring when the code is modified later. @@ -114,11 +114,11 @@ avoids the warning without touching your `package.json`. ::: - + - ```js title="workflows/sms-parser/parse-message.js" - export const FIELDS = ['id','name', 'dob', 'weight']; + ```js title="workflows/sms-parser/parse-message.js" + export const FIELDS = ['id', 'name', 'dob', 'weight']; export const parseSms = text => { const parts = text.trim().split('#'); @@ -135,7 +135,7 @@ avoids the warning without touching your `package.json`. ``` - + After `openfn compile --exports-only`: ```js title="dist/sms-parser/parse-message.mjs" diff --git a/docs/tutorials/tutorial.md b/docs/tutorials/tutorial.md index 00443ad3e680..ee5eee9bd2e1 100644 --- a/docs/tutorials/tutorial.md +++ b/docs/tutorials/tutorial.md @@ -20,7 +20,7 @@ sidebar_label: Workflow QuickStart workflow 6. In the `Input` panel on the left, add a custom input (e.g., a payload from a webhook request) or simply add empty brackets (`{}`) to run a Workflow with a - cron trigger. See the [Workflow docs](docs/build/workflows.md) for help with + cron trigger. See the [Workflow docs](/build/workflows.md) for help with running and testing Workflow. 7. If the Step suceeds, navigate back to the Canvase view and click the `+` icon to add a second Step. diff --git a/docusaurus.config.js b/docusaurus.config.js index fdf1a521f462..fbe5fad0fce5 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -2,8 +2,6 @@ const path = require('path'); module.exports = { title: 'OpenFn/docs', - tagline: - 'The leading digital public good for workflow automation, OpenFn makes ICT4D more efficient.', url: 'https://docs.openfn.org', baseUrl: '/', trailingSlash: false, @@ -12,6 +10,26 @@ module.exports = { favicon: 'img/favicon.ico', organizationName: 'openfn', projectName: 'docs', + // --- i18n (internationalization) --- + // Spanish is built at /es/ and linked from the navbar's language dropdown. + // Translated content lives in i18n//. Anything not translated + // falls back to the English source automatically. + i18n: { + defaultLocale: 'en', + locales: ['en', 'es'], + localeConfigs: { + en: { + label: 'English', + direction: 'ltr', + htmlLang: 'en', + }, + es: { + label: 'Español', + direction: 'ltr', + htmlLang: 'es', + }, + }, + }, markdown: { hooks: { onBrokenMarkdownLinks: 'warn' }, mermaid: true, @@ -75,6 +93,10 @@ module.exports = { type: 'docsVersionDropdown', position: 'right', }, + { + type: 'localeDropdown', + position: 'right', + }, { href: 'https://github.com/openfn/docs', position: 'right', @@ -151,6 +173,9 @@ module.exports = { sidebarPath: require.resolve('./sidebars-main.js'), routeBasePath: '/documentation', editUrl: 'https://github.com/openfn/docs/edit/main', + // Point "Edit this page" at the translated file rather than the + // English source when reading a non-default locale. + editLocalizedFiles: true, lastVersion: 'current', versions: { current: { diff --git a/glossary.yml b/glossary.yml index 9241b08c4d0f..90ff040a0be2 100644 --- a/glossary.yml +++ b/glossary.yml @@ -5,8 +5,9 @@ # Product vocabulary for the OpenFn docs. Two consumers read this file: # # 1. The translate skill (.agents/skills/translate/SKILL.md). Any term with -# `translate: false` must appear verbatim in every translated page. -# 2. The house style in AGENTS.md. Any spelling in `variants` is replaced +# `translate: false` must appear verbatim in every translated page, +# unless `locales` gives a word for that page's locale. +# 2. The house style in STYLE.md. Any spelling in `variants` is replaced # with `term` in English pages. # # Humans maintain this file. Add a term when a review shows the same @@ -25,9 +26,9 @@ # case_sensitive: boolean # true = the house-style check flags case variants too (default false) # variants: [string] # spellings the house-style check flags, to replace with `term` # note: string # guidance for humans and the agent -# locales: # optional. Only used when translate: true, to pin a -# es: string # specific rendering per locale instead of free -# fr: string # translation. +# locales: # optional. The word a locale uses instead of the +# es: string # English. Overrides translate: false for that +# fr: string # locale only; other locales keep the English. # # patterns: # regexes that are never translated, for families # - pattern: string # of identifiers too numerous to list (adaptor @@ -87,7 +88,12 @@ terms: - term: credential translate: false product_noun: true - note: Stored authentication configuration attached to a Step. + note: >- + Stored authentication configuration attached to a Step. Spanish translates + it because "credencial" looks almost the same as the English in the app. + Decide separately for each new locale. + locales: + es: credencial - term: trigger translate: false @@ -131,7 +137,12 @@ terms: - term: project translate: false product_noun: true - note: Administrative grouping of workflows, credentials, and collaborators. + note: >- + Administrative grouping of workflows, credentials, and collaborators. + Spanish translates it because "proyecto" looks almost the same as the + English in the app. Decide separately for each new locale. + locales: + es: proyecto - term: dataclip translate: false @@ -143,7 +154,12 @@ terms: - term: collection translate: false product_noun: true - note: The Collections key-value store feature. Ordinary English use may be translated. + note: >- + The Collections key-value store feature. Spanish translates it because + "colección" looks almost the same as the English in the app. Decide + separately for each new locale. + locales: + es: colección - term: sandbox translate: false @@ -175,7 +191,12 @@ terms: - term: operation translate: false product_noun: true - note: A function exported by an adaptor, e.g. `get()`, `upsert()`. + note: >- + A function exported by an adaptor, e.g. `get()`, `upsert()`. Spanish + translates it because "operación" looks almost the same as the English in + the app. Decide separately for each new locale. + locales: + es: operación - term: KoboToolbox translate: false diff --git a/i18n/es/code.json b/i18n/es/code.json new file mode 100644 index 000000000000..5375475f9971 --- /dev/null +++ b/i18n/es/code.json @@ -0,0 +1,86 @@ +{ + "homepage.highlights.jobWritingGuide.title": { + "message": "Guía para escribir jobs" + }, + "homepage.highlights.jobWritingGuide.description": { + "message": "¿Vas a escribir un job para OpenFn? Empieza aquí" + }, + "homepage.highlights.cliUsage.title": { + "message": "Ejemplos de uso de la CLI" + }, + "homepage.highlights.cliUsage.description": { + "message": "Mira de un vistazo lo que puede hacer la CLI" + }, + "homepage.highlights.javascriptTips.title": { + "message": "Trucos y consejos de JavaScript" + }, + "homepage.highlights.javascriptTips.description": { + "message": "Mejora tu código" + }, + "homepage.features.docs.title": { + "message": "Documentación" + }, + "homepage.features.docs.description": { + "message": "Documentación sobre todos los aspectos de OpenFn, el bien público digital líder en automatización de flujos de trabajo." + }, + "homepage.features.adaptors.title": { + "message": "Adaptors" + }, + "homepage.features.adaptors.description": { + "message": "Documentación, ejemplos, registros de cambios y resúmenes de los adaptors, fáciles de buscar y explorar, para conectar los DPG más usados del mundo." + }, + "homepage.features.articles.title": { + "message": "Artículos" + }, + "homepage.features.articles.description": { + "message": "¿Cómo prepararse para una integración de datos? ¿Cómo estructurar los ID externos? ¿Cómo...?" + }, + "homepage.features.blog.title": { + "message": "Blog" + }, + "homepage.features.blog.description": { + "message": "Ayudamos a las intervenciones de impacto social más prometedoras del mundo a crecer mediante la automatización, la integración de datos y la interoperabilidad. Estas son sus historias." + }, + "homepage.features.enterprise.title": { + "message": "Empresas" + }, + "homepage.features.enterprise.description": { + "message": "Descubre la plataforma de integración como servicio (iPaaS) de OpenFn para empresas, con planes gratuitos para siempre y opciones asequibles para crecer." + }, + "homepage.meta.title": { + "message": "Inicio" + }, + "homepage.meta.description": { + "message": "El sitio de documentación de OpenFn" + }, + "homepage.hero.title": { + "message": "Documentación de OpenFn" + }, + "homepage.hero.subtitle": { + "message": "OpenFn, el bien público digital líder en automatización de flujos de trabajo, hace que ICT4D sea más eficiente." + }, + "homepage.hero.cta": { + "message": "Comenzar" + }, + "homepage.newsletter.imageAlt": { + "message": "Boletín" + }, + "homepage.newsletter.title": { + "message": "Boletín" + }, + "homepage.newsletter.description": { + "message": "No te pierdas ninguna de nuestras historias: suscríbete a nuestro boletín aquí." + }, + "homepage.newsletter.emailPlaceholder": { + "message": "Correo electrónico" + }, + "homepage.newsletter.subscribe": { + "message": "Suscribirse" + }, + "homepage.highlights.heading": { + "message": "✨Destacados de la documentación✨" + }, + "untranslatedNotice.message": { + "message": "Esta página no tiene versión en español, así que te la mostramos en inglés." + } +} diff --git a/i18n/es/docusaurus-plugin-content-blog-articles/options.json b/i18n/es/docusaurus-plugin-content-blog-articles/options.json new file mode 100644 index 000000000000..18375b3a3f5a --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-blog-articles/options.json @@ -0,0 +1,14 @@ +{ + "title": { + "message": "Blog", + "description": "The title for the blog used in SEO" + }, + "description": { + "message": "Blog", + "description": "The description for the blog used in SEO" + }, + "sidebar.title": { + "message": "Publicaciones recientes", + "description": "The label for the left sidebar" + } +} diff --git a/i18n/es/docusaurus-plugin-content-blog/options.json b/i18n/es/docusaurus-plugin-content-blog/options.json new file mode 100644 index 000000000000..18375b3a3f5a --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-blog/options.json @@ -0,0 +1,14 @@ +{ + "title": { + "message": "Blog", + "description": "The title for the blog used in SEO" + }, + "description": { + "message": "Blog", + "description": "The description for the blog used in SEO" + }, + "sidebar.title": { + "message": "Publicaciones recientes", + "description": "The label for the left sidebar" + } +} diff --git a/i18n/es/docusaurus-plugin-content-docs/current.json b/i18n/es/docusaurus-plugin-content-docs/current.json new file mode 100644 index 000000000000..0c458c6b9742 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current.json @@ -0,0 +1,62 @@ +{ + "version.label": { + "message": "v2 ⚡", + "description": "The label for version current" + }, + "sidebar.docs.category.Get Started": { + "message": "Primeros pasos", + "description": "The label for category 'Get Started' in sidebar 'docs'" + }, + "sidebar.docs.category.Tutorials": { + "message": "Tutoriales", + "description": "The label for category 'Tutorials' in sidebar 'docs'" + }, + "sidebar.docs.category.Design Workflows": { + "message": "Diseñar workflows", + "description": "The label for category 'Design Workflows' in sidebar 'docs'" + }, + "sidebar.docs.category.Write Jobs": { + "message": "Escribir jobs", + "description": "The label for category 'Write Jobs' in sidebar 'docs'" + }, + "sidebar.docs.category.Platform ⚡": { + "message": "Plataforma ⚡", + "description": "The label for category 'Platform ⚡' in sidebar 'docs'" + }, + "sidebar.docs.category.Build & Manage Workflows": { + "message": "Crear y gestionar workflows", + "description": "The label for category 'Build & Manage Workflows' in sidebar 'docs'" + }, + "sidebar.docs.category.Monitor History": { + "message": "Supervisar el historial", + "description": "The label for category 'Monitor History' in sidebar 'docs'" + }, + "sidebar.docs.category.Manage Projects": { + "message": "Gestionar proyectos", + "description": "The label for category 'Manage Projects' in sidebar 'docs'" + }, + "sidebar.docs.category.Manage Users & Credentials": { + "message": "Gestionar usuarios y credenciales", + "description": "The label for category 'Manage Users & Credentials' in sidebar 'docs'" + }, + "sidebar.docs.category.CLI": { + "message": "CLI", + "description": "The label for category 'CLI' in sidebar 'docs'" + }, + "sidebar.docs.category.Deployment": { + "message": "Despliegue", + "description": "The label for category 'Deployment' in sidebar 'docs'" + }, + "sidebar.docs.category.Migrate to v2": { + "message": "Migrar a v2", + "description": "The label for category 'Migrate to v2' in sidebar 'docs'" + }, + "sidebar.docs.category.Contribute": { + "message": "Contribuir", + "description": "The label for category 'Contribute' in sidebar 'docs'" + }, + "sidebar.docs.link.Community Forum": { + "message": "Foro de la comunidad", + "description": "The label for link 'Community Forum' in sidebar 'docs', linking to 'https://community.openfn.org'" + } +} diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-challenges.md b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-challenges.md new file mode 100644 index 000000000000..e35c7b195a47 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-challenges.md @@ -0,0 +1,275 @@ +--- +title: Desafíos de la CLI +sidebar_label: Desafíos de la CLI +slug: /cli-challenges +translation_source_hash: 77369268e959643bb1252b91b59cf8176063c423 +translation_review_status: machine +--- + +#### Resuelve problemas reales y demuestra tus habilidades con la línea de comandos participando en nuestros desafíos de la CLI {#solve-real-world-problems-and-showcase-your-command-line-skills-by-participating-in-our-cli-challenges} + +:::tip Notas importantes + +- Un desarrollador con algo de experiencia en JavaScript debería poder escribir, + ejecutar y depurar jobs complejos de varios pasos con OpenFn, usando solo un + editor de texto y su terminal. +- Si te quedas atascado y necesitas ayuda, publica en + [community.openfn.org](https://community.openfn.org). +
+ Expande para ver la plantilla de reporte de errores + + ```markdown + Subject: Bug Report - [Brief Description] + + **Description:** [Concise description of the bug.] + + **Steps to Reproduce:** + + 1. + 2. + 3. + + **Environment:** + + - OS: [e.g., Windows 10] + - CLI: [e.g., v0.4.11] + - Node: [e.g., v 18.17.1] + - NPM: [e.g., 8.19.2] + + **Attachments:** [Screenshots, error messages, or relevant files.] + ``` + +
+ +::: + +### 🏆 Crea un saludo personalizado {#-create-personalized-greeting} + +**Descripción general:** + +Crea un nuevo job `hello.js` que muestre un saludo personalizado con tu nombre. + +**Objetivo:** + +Escribe un job de OpenFn con el [adaptor common](/adaptors/packages/common-docs) +que muestre un mensaje de saludo con tu nombre. + +**Requisitos:** + +1. Instala la última versión del adaptor common. + + ``` + openfn repo install @openfn/language-common + ``` + +**Tareas:** + +1. Crea un archivo nuevo llamado `hello.js`. +2. Escribe un script de JavaScript en `hello.js` que genere un saludo con tu + nombre. +3. Ejecuta el job con el comando `openfn hello.js -a common -o tmp/output.json`. +4. Confirma que se ejecutó correctamente. + +**Lista de verificación:** + +- [ ] Creaste el archivo nuevo `hello.js`. +- [ ] Escribiste en `hello.js` un script de JavaScript para un saludo + personalizado. +- [ ] Ejecutaste el job con el comando indicado. +- [ ] Comprobaste que los logs de la salida de la CLI son correctos. + +--- + +### 🏆 Obtén e inspecciona datos por HTTP {#-fetch-and-inspect-data-via-http} + +**Descripción general:** + +Escribe un job que obtenga datos de usuarios de la +[API de JSONPlaceholder](https://jsonplaceholder.typicode.com/users) con el +[adaptor http](/adaptors/packages/http-docs) de OpenFn. + +**Objetivo:** + +Obtén y muestra los detalles del primer usuario de la API de JSONPlaceholder. + +**Requisitos:** + +1. Instala la última versión del adaptor http. + +```bash +openfn repo install @openfn/language-http +``` + +2. Usa la [API de JSONPlaceholder](https://jsonplaceholder.typicode.com/users). +3. Crea un archivo llamado `getUsers.js` que contenga el script. + +**Tareas:** + +1. Crea un archivo (`getUsers.js`) para el script. +2. Obtén una lista de usuarios de la API de JSONPlaceholder. +3. Muestra los detalles del primer usuario. +4. Ejecuta el job con OpenFn/cli: + `openfn getUsers.js -a http -o tmp/output.json`. +5. Comprueba que los logs de la CLI son los esperados. + +**Lista de verificación:** + +- [ ] Obtuviste los datos de los usuarios. +- [ ] Mostraste correctamente los detalles del primer usuario. +- [ ] Usaste correctamente las funciones del adaptor http de OpenFn. +- [ ] Comprobaste que los logs de la salida de la CLI son correctos. + +--- + +### 🏆 Obtén metadatos de COVID-19 {#-retrieve-covid-19-metadata} + +**Descripción general:** + +Obtén y presenta metadatos de COVID-19 con la +[API del COVID Tracking Project de The Atlantic](https://covidtracking.com/data/api). + +**Objetivo:** + +Escribe un job que obtenga datos de COVID-19 de la API y calcule algunos valores +agregados para el período que elijas. + +**Requisitos:** + +1. Instala la última versión del adaptor http. + +```bash +openfn repo install @openfn/language-http +``` + +**Tareas:** + +1. Escribe una operación de OpenFn que obtenga metadatos de COVID-19 de la + [API del COVID Tracking Project de The Atlantic](https://covidtracking.com/data/api). + - Usa `https://api.covidtracking.com` como tu **baseUrl** en + `state.configuration`. +2. Ejecuta el job con la CLI de OpenFn mediante el comando + `openfn your_operation_file.js -a http -o tmp/output.json`. +3. Evalúa la salida y prueba distintas formas de dar formato a los datos de + COVID-19 por región o de presentarlos. + +**Lista de verificación:** + +- [ ] Creaste un archivo de operación de OpenFn. +- [ ] Escribiste el código para obtener metadatos de COVID-19 de la API + indicada. +- [ ] Ejecutaste el job con el comando de la CLI indicado. +- [ ] Probaste varias opciones de formato o presentación para los datos + obtenidos. + +> Experimenta con la presentación de los datos para entenderlos mejor. ¡Buena +> suerte! 🌐🦠 + +--- + +### 🏆 Extrae nombres y correos electrónicos {#-extract-names--emails} + +**Descripción general:** + +En este desafío, usarás la API de JSONPlaceholder para obtener los comentarios +de una publicación concreta (la publicación con ID 1). Tu tarea es extraer los +campos "name" y "email" de cada comentario y registrar en el log los datos +extraídos. + +**Objetivo:** + +Escribe un job que obtenga los comentarios de la publicación con ID 1, extraiga +los campos "name" y "email" de cada comentario y registre en el log los datos +extraídos. + +**Requisitos:** + +- Conocimientos básicos de JavaScript. +- La CLI de OpenFn instalada en tu computadora. + +**Tareas:** + +1. **Obtén los comentarios de la publicación:** + + - Agrega una operación que obtenga todos los comentarios de la publicación + con ID 1 de la + [API de JSONPlaceholder](https://jsonplaceholder.typicode.com/posts/1/comments). + +2. **Extrae el nombre y el correo electrónico:** + + - Escribe una función que extraiga los campos "name" y "email" de cada + comentario. + +3. **Registra los datos extraídos:** + - Muestra en la consola los datos extraídos (nombre y correo electrónico) de + cada comentario. + +**Lista de verificación:** + +- [ ] Obtuviste los comentarios de la publicación con ID 1. +- [ ] Escribiste una función que extrae "name" y "email" de los comentarios. +- [ ] Mostraste en la consola los datos extraídos. + +--- + +### 🏆 Controla los mensajes de error {#-control-error-messages} + +Depura qué causa un error en la siguiente línea de código y muestra el mensaje +de error. + +```jsx +// Get post where id is 180 +get('posts/180'); +``` + +--- + +### 🏆 Transformación y limpieza de datos {#-data-transformation-and-cleaning} + +**Descripción general:** + +En este desafío, usarás métodos globales de arrays de JavaScript, en concreto +`Array.reduce`, `Array.filter` o `Array.map`, para crear una serie de +operaciones que obtengan publicaciones y las filtren por ID de usuario. + +**Objetivo:** + +Escribe un job que obtenga las publicaciones de un ID de usuario concreto, `1`. + +**Requisitos:** + +1. Usa la API de JSONPlaceholder `https://jsonplaceholder.typicode.com`. +2. Instala la última versión del adaptor http. + +``` +openfn repo install @openfn/language-http +``` + +**Tareas:** + +1. **Crea el archivo:** + + - Crea un archivo llamado `getPosts.js` para tu job. + +2. **Obtén todas las publicaciones:** + + - Agrega la primera operación para obtener todas las publicaciones. Usa la + API indicada o cualquier otra fuente que elijas que ofrezca una lista de + publicaciones. + +3. **Filtra las publicaciones por ID:** + + - Agrega una segunda operación con una función que filtre las publicaciones + por ID de usuario. Puedes usar `Array.filter` o cualquier otro método + adecuado. + +4. **Obtén las publicaciones del ID de usuario 1:** + + - Usa la función de la segunda operación para filtrar las publicaciones del + ID de usuario 1. + +**Lista de verificación:** + +- [ ] Creaste el archivo `getPosts.js`. +- [ ] Obtuviste todas las publicaciones. +- [ ] Escribiste una función que filtra publicaciones por ID de usuario. +- [ ] Obtuviste las publicaciones del ID de usuario 1. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-collections.md b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-collections.md new file mode 100644 index 000000000000..bda63281f9bb --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-collections.md @@ -0,0 +1,315 @@ +--- +title: Uso de colecciones con la CLI +sidebar_label: Colecciones +slug: /collections-cli +translation_source_hash: da22d0b4b6f41876daff9101ed14f28cd796084d +translation_review_status: machine +--- + +La CLI de OpenFn permite leer y escribir en +[colecciones](/build/collections.md): un almacén de clave/valor integrado en +OpenFn. + +:::caution Versiones + +La compatibilidad con colecciones se agregó a la CLI en la versión 1.9.0. + +Ejecuta `npm install -g @openfn/cli` para actualizarla o instalarla. + +::: + +Puedes usar la CLI para: + +- Explorar el contenido de las colecciones sin ejecutar un workflow +- Probar la sintaxis de consulta para obtener las claves que necesitas +- Actualizar objetos de mapeo y tablas de búsqueda a partir de archivos locales + (o bajo control de versiones) +- Eliminar datos manualmente + +:::tip + +¿Tienes comentarios? ¿Quieres más compatibilidad con colecciones en la CLI? +¡Publica una solicitud de funcionalidad en +[community.openfn.org](https://community.openfn.org/c/feature-requests)! + +::: + +Empieza a usar la API de colecciones con `openfn collections --help`. + +Necesitarás un token de acceso personal (PAT) para acceder a una colección. +También tienes que asegurarte de que la colección exista antes de poder leerla o +escribir en ella. Consulta +[Gestionar colecciones](/build/collections.md#managing-collections). + +:::info ¿Quieres usar colecciones en un workflow de la CLI? + +Esta documentación explica cómo usar el comando `openfn collections` de la CLI. + +Si ejecutas una expresión o un workflow con la CLI, tienes que usar el adaptor +de colecciones. Consulta la +[documentación del adaptor de colecciones](/adaptors/collections#cli-usage) para +más detalles + +::: + +## Obtener un PAT {#getting-a-pat} + +Los datos de las colecciones se guardan de forma segura dentro de un proyecto, y +solo pueden acceder a ellos los usuarios con acceso a ese proyecto. Así que, si +quieres acceder a una colección, tienes que decirle al servidor quién eres. + +Para eso usamos los tokens de acceso personal. Consulta +[Crear y administrar tokens de API](/manage-users/api-tokens.md#about-api-tokens) +para más detalles. + +Cuando tengas un PAT, tienes que pasárselo a la CLI. La forma más fácil es +definir la variable de entorno `OPENFN_API_KEY` o usar un archivo `.env`. La CLI +usará este valor automáticamente en todas las solicitudes. + +También puedes pasar `--token` a la CLI para reemplazar el valor cargado de tu +entorno. + +```bash +openfn collections get my-collection \* --token $MY_OPENFN_PAT +``` + +:::tip + +El resto de esta guía da por hecho que la variable de entorno `OPENFN_PAT` está +definida. Si lo está y usas un servidor que tiene una colección `my-collection`, +todos los ejemplos funcionarán. + +::: + +## Definir un servidor {#setting-a-server} + +De forma predeterminada, la CLI apunta a nuestra aplicación en la nube en +https://app.openfn.org. + +Si usas la versión de código abierto u otro despliegue, también tendrás que +indicarle a la CLI qué servidor de colecciones usar. + +Puedes hacerlo pasando `--endpoint` directamente: + +```bash +openfn collections get my-collection \* --endpoint http://localhost:4000 +``` + +O definiendo la variable de entorno `OPENFN_ENDPOINT`. + +:::tip + +Para ver qué servidor está usando la CLI, pide logs de nivel debug en la salida: + +```bash +openfn collections get my-collection \* --log debug +``` + +::: + +## Nombres únicos por proyecto {#project-name-uniqueness} + +En las versiones de Lightning anteriores a la 2.17.0, los nombres de las +colecciones eran únicos en toda la instancia. + +Desde la 2.17.0, los nombres de las colecciones son únicos dentro de un +proyecto, así que una instancia de OpenFn puede tener varias colecciones con el +mismo nombre. + +Cualquier solicitud a la API de colecciones intenta resolver un nombre de +colección a una sola colección. Pero si hay conflictos, el servidor devuelve un +código de error 409. + +Para resolverlo, pasa un id de proyecto: + +```bash +openfn collections get --project-id 1d28c76c-e4ef-4e58-ac1e-464dc479946c +``` + +También puedes definir el ID del proyecto con una variable de entorno, o usar el +atajo `-p`. + +## Obtener elementos {#fetching-items} + +Puedes obtener elementos de una colección pasando un nombre de colección y una +clave, o un patrón de claves (como `*` para "todo", o `2024*` para las claves +que empiezan por `2024`). + +```bash +openfn collections get +``` + +Por ejemplo, para obtener todo de `my-collection`, ejecuta: + +```bash +openfn collections get my-collection \* +``` + +:::tip + +En las terminales de Unix (macOS o Linux), el carácter `*` tiene un significado +especial. Así que, si quieres obtener todos los elementos, tienes que escaparlo +o ponerlo entre comillas: + +``` +openfn collections get my-collection \* +``` + +Incluir `*` en un patrón debería seguir funcionando: + +``` +openfn collections get my-collection 2024* +``` + +::: + +Los elementos de las colecciones se guardan como cadenas, pero se serializan a +JSON en la salida. + +De forma predeterminada, la CLI muestra los valores descargados en tu terminal. +Para escribirlos en disco, pasa `--output` o `-o` con una ruta de archivo +relativa a tu directorio de trabajo: + +```bash +openfn collections get my-collection \* -o /tmp/my_collection.json +``` + +Para dar formato a la salida y que sea más fácil de leer, agrega la opción +`--pretty`: + +```bash +openfn collections get my-collection \* -o /tmp/my_collection.json --pretty +``` + +Es importante entender que la salida funciona un poco distinto si obtienes un +elemento concreto con una sola clave o si obtienes muchos elementos con un +patrón de claves. + +Una sola clave siempre devuelve su valor "en bruto" o "tal cual", sin la clave. +Así que, para una clave `item-1` cuyo valor es un objeto JSON, esto: + +```bash +openfn collections get my-collection item-1 +``` + +Descargará y guardará algo como esto: + +```js +{ + "id": "item-1" + /* ... other properties of the value */ +} +``` + +Si usas un patrón de claves para obtener datos, el valor se devuelve en modo +multielemento: un objeto JSON donde la clave es la clave del elemento y el valor +es el valor del elemento. + +Así que, si obtienes todos los elementos cuya clave empieza por `item-`: + +```bash +$ openfn collections get my-collection item-1* +``` + +Los datos resultantes se verán así: + +```json +{ + "item-1": { + "id": "item-1" + /* ... other properties of the value */ + }, + "item-10": { + "id": "item-10" + /* ... other properties of the value */ + } +} +``` + +## Subir elementos {#uploading-items} + +Puedes usar el comando `collections` para subir datos a una colección. Al subir, +los valores siempre vienen de un archivo en disco. En este ejemplo se usan +archivos JSON, pero si subes un solo valor, no hace falta que sea JSON válido. + +El comando `set` tiene dos modos. Para subir un solo elemento, usa: + +```bash +openfn collections set +``` + +Esto lee los datos de `path/to/value.json` como una cadena y hace un upsert con +la clave indicada. No se admiten patrones de claves. + +Para hacer un upsert masivo de varios valores, usa: + +```bash +openfn collections set --items +``` + +El archivo `items.json` tiene que contener un objeto JSON donde las claves son +las claves de los elementos y los valores son los valores de los elementos +(igual que lo que devuelve el comando get multielemento): + +```json +{ + "item-1": { + "id": "item-1" + /* ... other properties of the value */ + }, + "item-10": { + "id": "item-10" + /* ... other properties of the value */ + } +} +``` + +:::tip + +Recuerda que las colecciones siempre usan una estrategia de _upsert_ al subir +elementos nuevos. + +Es decir, si una clave no existe, se crea y se le asigna un valor. Si ya existe, +se actualiza su valor. + +::: + +## Eliminar elementos {#removing-items} + +También puedes eliminar elementos de una colección con el comando +`collections remove`: + +```bash +openfn collections remove +``` + +Se admiten patrones de claves, que te permiten eliminar varias claves. + +Usa `--dry-run` para obtener una lista de las claves que se eliminarían, sin +eliminarlas realmente: + +```bash +openfn collections remove my-collection 2024* --dry-run +``` + +## Solución de problemas {#troubleshooting} + +### Error 409: varios nombres de colección coinciden {#error-409-multiple-collection-names-matched} + +Esto significa que pediste una colección por su nombre, pero el servidor tiene +varias colecciones con ese nombre. + +Tienes que limitar tu solicitud al proyecto correcto incluyendo el id del +proyecto en la solicitud. + +Puedes pasarlo directamente: + +``` +openfn collections get my-collection \* --project-id 1d28c76c-e4ef-4e58-ac1e-464dc479946c +``` + +O definir una variable de entorno (se admiten archivos `.env`): + +``` +OPENFN_PROJECT_ID=1d28c76c-e4ef-4e58-ac1e-464dc479946c +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-intro.md b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-intro.md new file mode 100644 index 000000000000..6fb5f7d5bbfb --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-intro.md @@ -0,0 +1,157 @@ +--- +title: Primeros pasos con la CLI de OpenFn +sidebar_label: Primeros pasos +slug: /cli +translation_source_hash: 944be784d36e841ea14f6d763d05830a7da98a18 +translation_review_status: machine +--- + +#### Crea y prueba tus workflows e integraciones automatizadas desde la línea de comandos. {#build-and-test-your-automated-workflows-and-integrations-via-the-command-line} + +La CLI de OpenFn es una herramienta para desarrolladores que te ayuda a crear, +probar y gestionar tus workflows directamente desde la línea de comandos. Es +fácil de instalar, funciona en macOS, Windows y Linux, y ofrece muchas +funcionalidades para mejorar tu experiencia de desarrollo con OpenFn. Con la CLI +de OpenFn puedes: + +- Sincronizar workflows entre OpenFn y un sistema de archivos local o GitHub +- Ejecutar workflows de OpenFn de forma segura +- Diagnosticar y depurar steps de OpenFn +- [Hacer pruebas unitarias del código de los jobs](/documentation/jobs/unit-testing-jobs) + con JavaScript estándar +- Leer y escribir datos de colecciones + +--- + +### Antes de empezar {#before-you-start} + +Antes de empezar con @openfn/cli, prepara algunas herramientas clave: + +1. **Editor de código:** asegúrate de tener un editor de código instalado en tu + computadora. Puedes usar editores populares como + [VS Code](https://code.visualstudio.com/) o + [Sublime](https://www.sublimetext.com/). +2. **Node.js:** instala Node.js (versión 24 o posterior). En Linux, Windows o + macOS, usa un gestor de versiones como [nvm](https://github.com/nvm-sh/nvm) o + [asdf](https://asdf-vm.com/guide/getting-started.html). También puedes + [instalar Node.js directamente](https://kinsta.com/blog/how-to-install-node-js/) + siguiendo esta guía. + +También deberías **entender los conceptos básicos de OpenFn**, en particular los +steps y los adaptors. Consulta la +[sección de introducción](/get-started/home.md) de este sitio para ponerte al +día. + +--- + +### Instalar la CLI {#install-the-cli} + +Para descargar la última versión de +[@openfn/cli](https://www.npmjs.com/package/@openfn/cli), ejecuta el siguiente +comando en la línea de comandos. + +```bash +npm install -g @openfn/cli +``` + +Comprueba que todo funciona ejecutando el workflow de prueba incluido: + +```bash +openfn test +``` + +La palabra `openfn` invoca la CLI. La palabra `test` invoca el comando de +prueba. + +
+Expande para ver la salida esperada + +``` +[CLI] ♦ Versions: + ▸ node.js 18.12.1 + ▸ cli 1.0.0 +[CLI] ℹ Running test workflow... +[CLI] ℹ Execution plan: +[CLI] ℹ { + "options": { + "start": "start" + }, + "workflow": { + "steps": [ + { + "id": "start", + "state": { + "data": { + "defaultAnswer": 42 + } + "expression": "const fn = () => (state) => { console.log('Starting computer...'); return state; }; fn()", + "next": { + "calculate": "!state.error" + } + }, + { + "id": "calculate", + "expression": "const fn = () => (state) => { console.log('Calculating to life, the universe, and everything..'); return state }; fn()", + "next": { + "result": true + } + }, + { + "id": "result", + "expression": "const fn = () => (state) => ({ data: { answer: state.data.answer || state.data.defaultAnswer } }); fn()" + } + ] + } +} + +[CLI] ✔ Compiled all expressions in workflow +[R/T] ℹ Executing undefined +[R/T] ℹ Starting step start +[JOB] ℹ Starting computer... +[R/T] ✔ Completed step start in 1ms +[R/T] ℹ Starting step calculate +[JOB] ℹ Calculating to life, the universe, and everything.. +[R/T] ✔ Completed step calculate in 1ms +[R/T] ℹ Starting step result +[R/T] ✔ Completed step result in 0ms +[CLI] ✔ Result: 42 +``` + +
+ +El resto de la salida es la CLI contándote lo que hace internamente. + +**Consultar la versión** + +```bash +openfn -v +``` + +**Obtener ayuda** + +```bash +openfn help +``` + +--- + +### Actualizar la CLI {#updating-the-cli} + +Para instalar una versión nueva directamente sobre la que tienes instalada, +ejecuta el siguiente comando. + +```bash +npm install -g @openfn/cli +``` + +--- + +### Solución de problemas {#troubleshooting} + +Si tienes problemas con la instalación, intenta desinstalar primero la versión +actual y luego volver a instalarla. + +```bash +npm uninstall -g @openfn/cli +npm install -g @openfn/cli +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-sync.md b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-sync.md new file mode 100644 index 000000000000..af2cc0b0e9e6 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-sync.md @@ -0,0 +1,539 @@ +--- +title: OpenFn Sync +sidebar_label: Sync +slug: /sync +translation_source_hash: e4dbcbf31dd1975afc33be952424a6476b240f73 +translation_review_status: machine +--- + +Los proyectos de OpenFn son totalmente portables, es decir, se pueden mover a +otros lugares. + +Puedes crear un proyecto en la aplicación, descargarlo a tu computadora para +desarrollar sin conexión, volver a subirlo a la aplicación o incluso desplegarlo +en otro servidor de OpenFn. + +A esto lo llamamos OpenFn Sync, y es una de las funcionalidades más potentes que +ofrecen los proyectos de OpenFn. + +## ¿Qué es un proyecto? {#what-is-a-project} + +Un proyecto es un conjunto de workflows que resuelve, automatiza o integra +alguna función de negocio. + +Un proyecto vive en la aplicación de OpenFn (ya sea en la instancia SaaS en la +nube o en una instancia desplegada de forma privada), pero también puede existir +como archivos en un sistema de archivos. + +Cada proyecto lleva asociados algunos metadatos (como un nombre y una +descripción) y cierta configuración, como credenciales y colecciones. + +Dentro de la aplicación de OpenFn, un proyecto es una entidad de nivel superior +facturable, y todos sus workflows y su configuración se guardan en tablas de la +base de datos. + +El proyecto de la aplicación incluye muchas cosas más: la configuración de +canales, el historial de runs, los dataclips guardados y las sesiones de chat +con el asistente de IA. + +El proyecto también puede existir en un sistema de archivos local. En ese caso, +es un conjunto de archivos que la CLI puede leer y ejecutar. Esta representación +local de un proyecto es bastante básica: aquí solo encontrarás workflows y +código. + +En el sistema de archivos pueden existir a la vez varios proyectos relacionados. +Cada uno vive en un único archivo de proyecto. Puedes hacer "checkout" o +"expandir" un proyecto a la vez en una carpeta local, lo que crea un archivo por +cada workflow y un archivo por cada step. + +Es habitual que un mismo proyecto conceptual (es decir, el código y la +configuración que impulsan una función de negocio) exista en varios lugares a la +vez. Puede tener varias representaciones en la aplicación a través de sandboxes, +tener una copia de seguridad en GitHub, ejecutarse localmente en la computadora +de un desarrollador y distribuirse a varias instancias remotas para ejecutarse +en producción. + +A veces llamamos espacio de trabajo a este conjunto de todos los proyectos +conocidos, relacionados y distribuidos. El problema de la sincronización +consiste en cómo se copian, despliegan o replican el código y la configuración +entre las instancias de un proyecto. + +No todos los elementos de un proyecto se incluyen en una sincronización. Por lo +general, sincronizamos los workflows del proyecto y algunas de sus opciones. +Pero no sincronizamos los datos asociados, los valores de las credenciales, el +historial de uso ni las sesiones de IA. + +## Estructura del proyecto {#project-structure} + +OpenFn Sync escribe un proyecto en el sistema de archivos siguiendo una serie de +convenciones. Tanto si usas la CLI como GitHub Sync, un proyecto tiene la +siguiente estructura: + +``` +├── openfn.yaml +├── .projects +│ ├── main@app.openfn.org.yaml +└── workflows + ├── my-workflow + │ ├── my-workflow.yaml + │ ├── my-step.js +``` + +En resumen, estos archivos son: + +- `openfn.yaml` declara que esta carpeta es un proyecto de OpenFn y contiene + metadatos y ajustes +- La carpeta `.projects` contiene una representación YAML completa de cada + proyecto +- La carpeta `workflows` muestra el contenido del proyecto: los steps, las + conexiones, etc. + +Veamos esta estructura con un poco más de detalle. + +### project.yaml {#projectyaml} + +El archivo de proyecto guarda una copia de todo el estado de un proyecto tal +como está guardado en la aplicación. Si lo abres, verás los workflows +representados como texto plano. + +El nombre de un archivo de proyecto tiene la forma `@.yaml`. El +alias es un nombre local que sirve para referirse a una versión concreta del +proyecto. El dominio es el de la instancia de OpenFn desde la que se descargó el +proyecto. + +No deberías editar el archivo de proyecto localmente, porque cualquier cambio se +perderá la próxima vez que lo obtengas. + +Puedes obtener tantos proyectos como quieras, y cada uno se guardará en su +propio archivo project.yaml. + +La carpeta `.projects` puede y debería incluirse en el control de versiones. + +### workflows {#workflows} + +Tener todo el proyecto dentro de un único archivo no es una buena forma de leer +o editar workflows. Por eso la CLI puede hacer "checkout" o "expandir" un +archivo de proyecto en el sistema de archivos. + +Hacer checkout es el proceso de escribir cada workflow en un archivo +workflow.yaml y cada step en un archivo step.js. Todo esto vive en el directorio +`workflows`. + +Aquí puedes editar los archivos todo lo que quieras, y los cambios se +registrarán cuando subas o despliegues de nuevo a la aplicación. + +Solo puedes hacer checkout de un proyecto a la vez. En realidad, esto viene muy +bien para trabajar con git, porque puedes hacer checkout de dos proyectos en +ramas distintas y compararlos o fusionarlos directamente entre sí. + +### workflow.yaml {#workflowyaml} + +Un archivo workflow.yaml define los steps de un workflow y las conexiones que +los unen. + +``` +id: my-workflow +name: My Workflow +start: webhook +steps: + - id: my-step + name: My Step + adaptor: '@openfn/language-http@7.2.9' + expression: ./my-step.js + - id: webhook + type: webhook + enabled: true + next: + my-step: + disabled: false + condition: always +``` + +La clave `next` de cada step define las conexiones de salida de ese step, es +decir, los steps que se ejecutan a continuación. En el ejemplo anterior, el step +`webhook` se ejecuta primero (lo determina la clave `start`) y define una única +conexión a `my-step`, que se ejecuta siempre. + +El código de cada step vive en su propio archivo .js. Puedes modificar el código +libremente y sincronizarlo de nuevo con el servidor en cualquier momento. Si +quieres cambiar el nombre de un step, asegúrate de actualizar el nombre del +archivo del step y la ruta de la clave `expression` en `workflow.yaml`. + +### openfn.yaml {#openfnyaml} + +Es un archivo de configuración de nivel superior que, en general, puedes +ignorar. Las herramientas de OpenFn lo usan para reconocer la carpeta raíz de un +proyecto. También contiene opciones de configuración para todos los proyectos +locales y metadatos sobre el proyecto que tienes con checkout. + +## Autorización {#authorization} + +Antes de usar la CLI para obtener algo de la aplicación, tendrás que +proporcionar una autorización. + +La mejor forma de hacerlo es definir una variable de entorno llamada +`OPENFN_API_KEY`. Dale el valor de tu +[token de acceso personal](/manage-users/api-tokens.md#about-api-tokens). + +:::info Tokens de acceso personal + +Consulta [Crear y administrar tokens de API](/manage-users/api-tokens.md) para +obtener ayuda con la configuración de un token. + +::: + +Si te conectas a varios proyectos o aplicaciones de OpenFn, puedes crear un +archivo `.env` y definir ahí las variables de entorno que necesites. La CLI +cargará este archivo e indicará qué claves está usando. Los valores de tu +archivo `.env` tienen prioridad sobre los definidos en tu sistema. + +También puedes pasar `--api-key` directamente como opción en la mayoría de los +comandos. + +:::info + +Esta guía da por hecho que quieres sincronizar con nuestra aplicación SaaS +alojada en [app.openfn.org](https://app.openfn.org) + +Puedes sincronizar con otra instancia de OpenFn definiendo la variable de +entorno `OPENFN_ENDPOINT` o pasando el argumento `--endpoint` en la mayoría de +los comandos. + +::: + +## Descargar un proyecto {#downloading-a-project} + +Para descargar un proyecto de la aplicación a tu computadora, ejecuta: + +```bash +openfn project pull +``` + +Esto crea un archivo en tu directorio de trabajo llamado +`.projects/main@app.openfn.org.yaml`. + +:::info + +Cada proyecto de la aplicación tiene un identificador único, llamado UUID, que +sirve para referirse a él. Es un número de 32 dígitos con la forma +`a6cc5bdd-b04f-4413-b4b8-132a5115acac` + +Puedes copiar el UUID de un proyecto desde la URL al abrirlo en la aplicación. +Es la cadena larga que va después de `projects`. + +Por ejemplo, el UUID es la parte en negrita de: +{'https://openfn.org/projects/'}{'abc087dd-3963-4260-8d09-ced2e1ff2bb0'}{'/w'} + +::: + +Después de descargar un proyecto por primera vez, no hace falta volver a indicar +el UUID. Puedes usar el alias, el id o dejar el identificador en blanco para +usar el proyecto que tienes con checkout. + +El comando `project pull` hace tres cosas: + +- Si no tienes un archivo `openfn.yaml`, crea uno +- _Obtiene_ (descarga) tu proyecto de la aplicación y lo guarda en un único + archivo en `.projects/main@app.openfn.org.yaml` +- Hace _checkout_ (expande) de ese proyecto en tu sistema de archivos, con cada + workflow y cada step en su propio archivo. + +## Alias {#aliases} + +En lugar de identificar un proyecto local con un UUID o un id largo, puedes usar +un _alias_. + +Cada proyecto local se guarda en un archivo como `main@app.openfn.org.yaml`, +donde la parte `main` es el alias local del proyecto. + +Puedes descargar un proyecto y definir el alias al mismo tiempo ejecutando: + +```bash +openfn project pull --alias dev +``` + +Esto guarda el proyecto en `dev@app.openfn.org.yaml`. + +Para cambiar el alias, basta con cambiar el nombre del archivo. Lo que vaya +antes de `@` se tratará como el alias. + +## Hacer checkout {#checking-out} + +Puedes hacer checkout de un proyecto en cualquier momento con: + +```bash +openfn project checkout +``` + +Esto actualiza tu carpeta local de workflows con el proyecto indicado. + +Si un checkout va a hacer que se pierdan cambios (es decir, cambiaste un archivo +step.js pero no lo desplegaste), recibirás una advertencia. Agrega `--force` +para ignorar el cambio, o ejecuta `openfn project clean` para borrar y +restablecer la carpeta `workflows`. + +El checkout solo modifica los archivos que gestiona la CLI, básicamente los +archivos de workflows y de steps. Si tienes otros archivos en el sistema de +archivos (como archivos de state o de pruebas), no se tocan. + +## Ejecutar workflows en proyectos {#running-workflows-in-projects} + +Puedes ejecutar cualquier workflow del proyecto con checkout por su nombre: + +```bash +openfn my-workflow +``` + +La CLI busca el workflow en tu carpeta `workflows` y lo ejecuta. Puedes pasar +state con `-s` y definir los niveles de log como de costumbre. + +Al ejecutar un workflow por su nombre de esta forma, obtienes dos ventajas: + +- Las **credenciales** se cargan automáticamente desde el mapa de credenciales + de `openfn.yaml`, así que no necesitas pasar `--credential-map` +- Las **colecciones** usan el servidor configurado en `openfn.yaml`, así que no + necesitas pasar `--collections-endpoint` ni nada más. + +## Desplegar un proyecto {#deploying-a-project} + +Para subir tus cambios locales de nuevo a la aplicación, ejecuta: + +```bash +openfn project deploy +``` + +Esto toma el proyecto que tienes con checkout y lo sube a la aplicación. También +indica qué cambió en el proyecto local. + +Antes de subirlo, la CLI obtiene la última versión del proyecto desde la +aplicación y comprueba si hay **divergencia**, es decir, si alguno de los +workflows que cambiaste localmente también se editó en la aplicación desde la +última vez que lo descargaste. Si es así, el despliegue falla con un error para +evitar que sobrescribas por accidente el trabajo de otra persona. + +Si quieres subirlo de todos modos, pasa `--force`: + +```bash +openfn project deploy --force +``` + +Para ver qué cambiaría sin subir nada, usa `--dry-run`. Esto registra en el log +el payload final de la actualización que se enviaría a la aplicación (como una +estructura JSON). + +Puedes desplegar el proyecto con checkout como un proyecto nuevo en la +aplicación de destino agregando la opción `--new`. Solo está disponible si +tienes privilegios de superusuario en la instancia de destino. + +También puedes desplegar el proyecto con checkout en otro proyecto de la +aplicación pasando su alias, id o uuid: + +``` +openfn project deploy main +``` + +Si tienes un sandbox de desarrollo con checkout, esto lo fusionaría directamente +en el proyecto principal de la aplicación. + +Ten en cuenta que tienes que haber obtenido el proyecto de destino localmente +antes de poder desplegarlo. + +## Despliegue avanzado {#advanced-deployment} + +De forma predeterminada, `openfn project deploy` toma el proyecto con checkout y +lo sube al servidor del que vino originalmente (según lo define el archivo de +proyecto). + +Pero también puedes usar deploy para sincronizar entre proyectos. Normalmente lo +harás para promover un sandbox de desarrollo o de staging a producción. Incluso +puedes usarlo para desplegar un proyecto de la aplicación SaaS en otra instancia +de OpenFn completamente distinta. + +### Desplegar directamente desde un archivo de spec o de state {#deploy-straight-from-a-spec-or-state-file} + +Con cualquier archivo de proyecto (por ejemplo, main@app.openfn.org.yaml) o una +spec exportada (en formato v1 o v2, tal como se exporta desde la configuración +de la aplicación), puedes desplegar directamente en otra instancia sin tener que +hacer checkout de nada antes. + +Solo tienes que pasar el nombre del archivo de origen como primer argumento. Si +tienes acceso de superusuario, puedes crear un proyecto nuevo así: + +``` +openfn project deploy dev@localhost.yaml --new --endpoint https://app.openfn.org +``` + +Si ya tienes un archivo de proyecto registrado localmente (lo tendrás si lo +obtuviste o descargaste antes), puedes pasar el alias (por ejemplo, `main`) para +desplegar en esa instancia. + +``` +openfn project deploy dev@localhost.yaml main +``` + +Lo más probable es que quieras forzar el despliegue, aunque se detecte +divergencia. Para eso, pasa la opción `-f`. + +Pasa `--no-confirm` o `-y` para omitir las preguntas de confirmación (esto es +importante si ejecutas scripts automatizados). + +### Gestionar credenciales {#managing-credentials} + +Gestionar las credenciales durante un despliegue puede ser complicado. + +Las credenciales del proyecto de origen TIENEN que existir en el proyecto de +destino; de lo contrario, se producirá un error. + +Todavía no hay forma de automatizar por completo la creación de credenciales, +porque plantea muchos problemas de seguridad. + +Sin embargo, la CLI ofrece algunas opciones. + +Puedes quitar por completo las credenciales del despliegue pasando +`--credentials none`. Ten en cuenta que el workflow no se ejecutará en el +destino hasta que se conecten manualmente las credenciales a los steps que las +necesitan. + +:::tip + +El argumento `--credentials` se puede pasar como `-c` o `--cred`. + +::: + +También puedes mapear credenciales, si el propietario o el nombre de las +credenciales es distinto en el sistema de destino. + +Puedes hacerlo con la CLI pasando un mapa separado por comas: + +``` +openfn project deploy spec.yaml --credentials a:service@openfn.org|cred-a,b:service@openfn.org|cred-b +``` + +Esto toma dos credenciales, `a` y `b`, las mapea a los nombres `cred-a` y +`cred-b` y cambia el propietario a `service@openfn.org`. + +También puedes definir estos mapeos en un archivo yaml (el mismo que se usa en +la ejecución). Define la clave `alias` debajo del identificador de la +credencial: + +``` +somedev@gmail.com|a: + alias: service@openfn.org|cred-a + +somedev@gmail.com|a: + alias: service@openfn.org|cred-b + +``` + +Luego pasa la ruta del archivo a la CLI con `--credentials`: + +``` +openfn project deploy spec.yaml --credentials credentials.yaml +``` + +## Sandboxes {#sandboxes} + +La CLI es totalmente compatible con los sandboxes. Trátalos como cualquier otro +proyecto: obtenlos la primera vez con su UUID. + +Usa el comando `checkout` para cambiar entre sandboxes y proyectos localmente. +Recuerda que solo puedes tener un proyecto con checkout a la vez. La CLI te +avisará si un checkout va a hacer que pierdas cambios locales. + +Al obtener un sandbox, el alias del proyecto será, de forma predeterminada, el +nombre del sandbox. + +Puedes fusionar dos proyectos localmente con `openfn project merge` y desplegar +el proyecto resultante en la aplicación (probablemente tendrás que forzar la +subida del cambio). Esto es útil para resolver conflictos. + +## Resolver conflictos de fusión {#resolving-merge-conflicts} + +A veces, fusionar un sandbox puede sobrescribir cambios en el proyecto de +destino. Esto puede pasar si un workflow del proyecto principal cambió _después_ +de que se volviera a crear el sandbox, así que el sandbox no lo conoce. Al +fusionar, se perdería ese cambio en main. + +Puedes resolver estos conflictos localmente con la CLI y git (u otro control de +versiones equivalente) y luego subir el proyecto resuelto a la aplicación. + +:::tip + +¡No necesitas un repositorio de GitHub para usar git! + +Git es simplemente un programa que se ejecuta en la terminal de tu sistema +local. + +GitHub es una aplicación alojada en la nube que a) ofrece acceso remoto a +repositorios git y b) ofrece una interfaz completa sobre un sistema de archivos +controlado por git. + +::: + +Así se hace con git. Este ejemplo da por hecho que quieres fusionar un sandbox +`dev` en tu proyecto principal `main`. + +- Asegúrate de tener lista una carpeta local para trabajar. Tiene que ser un + repositorio git. Ejecuta `git init` en cualquier carpeta para configurar git + (no necesitas un repositorio de GitHub conectado) +- Descarga tu proyecto principal localmente: `openfn project pull ` + (da por hecho que OPENFN_API_KEY está definida) +- Haz commit de tus cambios en git: + `git add . && git commit -m "checkout main project"` +- Ahora descarga tu sandbox localmente: `openfn project pull ` +- Esto hace que tu carpeta local `workflows/` se vea como tu sandbox +- Si ahora ejecutas `git status` y `git diff`, verás todos los cambios que se + aplicarían al proyecto principal al hacer la fusión +- Comprueba que estás conforme con las diferencias. Quizás quieras revertir + algunos archivos para que queden como en main + (`git checkout main workflows/my-workflow/job.js`). O quizás quieras combinar + a mano cambios de los dos proyectos. +- Cuando termines, sube el proyecto a la aplicación con la CLI: + `openfn project deploy main` +- Si quieres, puedes hacer commit de tus cambios en git (pero para este ejemplo + no hace falta) + +Para cambios más complejos, puedes probar este enfoque: + +- Descarga `main` localmente y haz commit +- Crea una rama nueva, y descarga `dev` y haz commit en esa rama +- Vuelve a la rama principal: `git checkout main` +- Fusiona la rama `dev` en main: `git merge dev` +- Resuelve los conflictos que indique git (cada conflicto de git debería + corresponder a un lugar donde tanto main como el sandbox hicieron cambios) +- Cuando termines, usa la CLI para forzar el despliegue de tus cambios + +## GitHub {#github} + +Puedes configurar un proyecto para que se sincronice automáticamente con GitHub. +Así, los commits en GitHub despliegan automáticamente los cambios en un proyecto +de OpenFn, y al presionar Save & Sync en la aplicación se hace commit de vuelta +en GitHub. + +Internamente, GitHub Sync usa los comandos `pull` y `deploy` de la CLI, que se +ejecutan desde GitHub Actions, para sincronizar tus proyectos. + +Ten en cuenta que, de forma predeterminada, GitHub Sync usa el formato antiguo, +con los archivos `state.json`, `project.yaml` y `config.json`. Al configurar un +nuevo GitHub Sync, puedes elegir el formato v2. La sincronización v2 solo sirve +para descargar un único proyecto por rama en GitHub, porque varios proyectos +sobrescribirían la misma carpeta `workflows/`. + +Consulta [Control de versiones](/manage-projects/link-to-gh.md) para obtener más +detalles sobre GitHub Sync. + +## Referencia rápida {#cheatsheet} + +| Comando | Descripción | +| ---------------------------------------- | ------------------------------------------------------------------------------------------ | +| `openfn project pull ` | Descarga un proyecto de la aplicación por primera vez | +| `openfn project pull` | Vuelve a descargar el proyecto actual | +| `openfn project pull --alias dev` | Descarga y define un alias local | +| `openfn project fetch ` | Obtiene un proyecto sin hacer checkout | +| `openfn project` | Lista todos los proyectos locales de la carpeta de trabajo actual | +| `openfn project checkout ` | Cambia a otro proyecto local | +| `openfn project deploy` | Despliega en la aplicación el proyecto con checkout | +| `openfn project deploy --dry-run` | Prueba un despliegue, pero omite el paso de subida | +| `openfn project deploy --force` | Fuerza la subida del proyecto con checkout, ignorando cualquier advertencia de divergencia | +| `openfn ` | Ejecuta un workflow del proyecto con checkout | +| `openfn project clean` | Borra la carpeta `workflows` y todo su contenido, y luego hace checkout del proyecto | diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-usage.md b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-usage.md new file mode 100644 index 000000000000..32c313659842 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-usage.md @@ -0,0 +1,293 @@ +--- +title: Uso básico de la CLI de OpenFn +sidebar_label: Uso básico +slug: /cli-usage +translation_source_hash: 0cf56f75d2c15049193e021867c97df6d9a9ed09 +translation_review_status: machine +--- + +Esta página muestra ejemplos de algunos de los usos más comunes de la CLI, como: + +- obtener ayuda +- ejecutar un job +- guardar el state +- ajustar el nivel de logs +- mantener el repositorio de adaptors +- ejecutar un workflow +- preparar el código de los jobs para pruebas unitarias +- cargar la documentación de un adaptor + +--- + +### Obtener ayuda {#get-help} + +```bash +openfn --help +``` + +```bash +openfn deploy --help +``` + +--- + +### Ejecutar un job {#run-a-job} + +Para ejecutar un solo job, tienes que indicar explícitamente qué adaptor usar. +Consulta los [adaptors disponibles públicamente](/adaptors). + +Si no se detecta la versión indicada, el adaptor se instala automáticamente. + +**Ejecutar un job con el adaptor http:** + +```bash +openfn path/to/job.js -a http +``` + +**Usar una versión concreta del adaptor:** + +```bash +openfn path/to/job.js -a http@2.0.0 +``` + +**Pasar la ruta de un adaptor instalado localmente:** + +```bash +openfn path/to/job.js -a http=/repo/openfn/adaptors/my-http-build +``` + +**Usar la compilación local del monorepo de adaptors:** + +```bash +openfn path/to/job.js -ma http +``` + +Tienes que indicar la ruta al monorepo en la variable de entorno +OPENFN_ADAPTORS_REPO. Por ejemplo: + +```bash +OPENFN_ADAPTORS_REPO=~/openfn/adaptors openfn job.js -ma http +``` + +Normalmente la defines en un archivo de configuración como `.profile` o +`.zshrc`. + +¡No olvides volver a compilar el adaptor antes de usarlo! + +**Ejecutar desde un step inicial concreto** + +Puedes indicar un step con su id exacto o con una parte del nombre o del id. + +```bash +openfn path/to/job.js --start cf628d9e -s path/to/input.json +``` + +Si ya guardaste en caché los resultados de este workflow, la CLI carga +automáticamente la entrada correcta desde la caché cuando omites el argumento +`-s`: + +```bash +openfn path/to/job.js --start cf628d9e +``` + +También puedes pasar `--end` para que el workflow termine antes. + +**Ejecutar un solo step** + +`--only` funciona igual que `--start` y `--end`. Puedes indicar una parte del +nombre o del id del step, y la entrada se carga automáticamente desde la caché. + +```bash +openfn path/to/job.js --only cf628d9e +``` + +--- + +### Gestionar el state de salida {#handle-output-state} + +Cuando termina el job, la CLI escribe el state resultante en el disco. De forma +predeterminada, crea un archivo `output.json` junto al archivo del job. + +**Puedes indicar rutas personalizadas para los archivos de salida y de state:** + +```bash +openfn path/to/job.js -a adaptor-name -o path/to/output.json -s path/to/state.json +``` + +**Usa `-O` para devolver la salida por stdout:** + +```bash +openfn path/to/job.js -a adaptor-name -O +``` + +**Guardar localmente los resultados de todos los steps** + +```bash +openfn path/to/workflow.json --cache-steps +``` + +Cada step escribe su salida en `./.cli-cache//.json`. +Git ignora la carpeta `.cli-cache`, y la caché se borra cuando vuelves a +ejecutar el workflow con `--cache-steps` habilitado. + +Para guardar en caché _siempre_, define la variable de entorno +`OPENFN_ALWAYS_CACHE_STEPS` como `"true"`, y pasa `--no-cache-steps` para +deshabilitarlo temporalmente. + +--- + +### Ajustar el nivel de logs {#adjust-logging-level} + +Puedes pasar `-l info` o `--log info` para obtener más información sobre lo que +ocurre durante la ejecución. Estos son los distintos niveles de logs: + +| nivel de logs | descripción | +| --------------------------------------------- | ------------------------------------------------------------ | +| `openfn path/to/job.js -a adaptor -l none` | Modo silencioso | +| `openfn path/to/job.js -a adaptor -l default` | Información general de lo que está ocurriendo | +| `openfn path/to/job.js -a adaptor -l info` | Más información sobre el runtime, la CLI y el job | +| `openfn path/to/job.js -a adaptor -l debug` | Información sobre el runtime, la CLI, el compilador y el job | + +--- + +### Mantener el repositorio de adaptors instalados automáticamente {#maintain-auto-installed-adaptors-repo} + +**Listar el contenido del repositorio:** + +```bash +openfn repo list +``` + +**Indicar la carpeta del repositorio con la variable de entorno +`OPENFN_REPO_DIR`:** + +```bash +export OPENFN_REPO_DIR=/path/to/repo +``` + +**Instalar adaptors automáticamente y comprobar si el repositorio tiene una +versión que coincida:** + +```bash +openfn path/to/job.js -a adaptor-name +``` + +**Eliminar todos los adaptors del repositorio:** + +```bash +openfn repo clean +``` + +--- + +### Ejecutar un workflow {#run-a-workflow} + +
+ Haz clic para ver la estructura JSON de un workflow de ejemplo + +```json +{ + "options": { + "start": "a" // optionally specify the start node (defaults to steps[0]) + }, + "workflow": { + "steps": [ + { + "id": "a", + "expression": "fn((state) => state)", // code or a path + "adaptor": "@openfn/language-common@1.75", // specify the adaptor to use (version optional) + "state": { + "data": {} // optionally pre-populate the data object (this will be overridden by keys in previous state) + }, + "configuration": {}, // Use this to pass credentials + "next": { + // This object defines which steps to call next + // All edges returning true will run + // If there are no next edges, the workflow will end + "b": true, + "c": { + "condition": "!state.error" // Note that this is an expression, not a function + } + } + } + ] + } +} +``` + +
+ +**Para ejecutar un workflow:** + +```bash +openfn path/to/workflow.json -o tmp/output.json +``` + +Consulta este +[tutorial](/build-for-developers/cli-walkthrough.md#7-running-workflows) +detallado sobre cómo ejecutar workflows con la CLI. + +--- + +### Preparar el código de los jobs para pruebas unitarias {#prepare-job-code-for-unit-testing} + +¿Quieres escribir pruebas unitarias para el código de tus jobs? Consulta +[Escribir pruebas unitarias para tus jobs](/documentation/jobs/unit-testing-jobs) +para ver la guía completa. + +Las pruebas unitarias solo funcionan con las funciones puras de tu código +(declaraciones de funciones de nivel superior): no funcionan con operaciones ni +con funciones de adaptors, porque estas necesitan servicios de backend +conectados. + +Para hacer pruebas unitarias de las funciones del código de tus jobs: + +1. **Compila tu proyecto** con `openfn compile`. Esto compila tus workflows y + los escribe como módulos ES normales. +2. **Importa las funciones compiladas** en tu archivo de pruebas, igual que + cualquier otro módulo JS nativo. +3. **Escribe las pruebas como siempre** para esas funciones puras. + +**Compila todos los workflows del proyecto y conserva solo las declaraciones +exportadas:** + +```bash +openfn compile --exports-only +``` + +Los archivos compilados se escriben en `dist/` como archivos `.mjs`. + +Con `--exports-only`, las operaciones se eliminan por completo y solo quedan las +funciones y variables exportadas. + +**Compila un solo workflow por nombre:** + +```bash +openfn compile my-workflow --exports-only +``` + +**Vuelve a compilar cada vez que cambie el código de un job:** + +```bash +openfn compile --exports-only --watch +``` + +Sin `--exports-only`, obtienes la salida compilada completa del step. + +### Cargar la documentación de un adaptor {#load-adaptor-documentation} + +La CLI puede mostrar la documentación de un adaptor en la terminal. Ten en +cuenta que primero tiene que descargar el adaptor al repositorio (si todavía no +está ahí), lo que puede tardar un momento. + +**Mostrar una lista de las funciones del adaptor** + +```bash +openfn docs http +``` + +**Mostrar la documentación de una función concreta** + +```bash +openfn docs http post +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-walkthrough.md b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-walkthrough.md new file mode 100644 index 000000000000..cadf6af2af85 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/cli-walkthrough.md @@ -0,0 +1,902 @@ +--- +title: Recorrido por la CLI +sidebar_label: Recorrido por la CLI +slug: /cli-walkthrough +translation_source_hash: a044b0a82a85cef420e88d48a916f20e81f5ddb0 +translation_review_status: machine +--- + +### 1. Primeros pasos con la CLI {#1-getting-started-with-the-cli} + +:::info Para empezar con @openfn/cli + +1. Crea una carpeta nueva para el repositorio en el que vas a trabajar con este + comando: `mkdir devchallenge && cd devchallenge` + +2. Aunque puedes guardar tus scripts de jobs en cualquier lugar, es buena + práctica guardar `state.json` y `output.json` en una carpeta `tmp`. Para + hacerlo, crea un directorio llamado `tmp` dentro de tu carpeta + `devchallenge`: `mkdir tmp` + +3. Como `state.json` y `output.json` pueden contener información de + configuración sensible y datos del proyecto, es importante no subirlos nunca + a GitHub. Para que GitHub ignore estos archivos, agrega el directorio `tmp` a + tu archivo `.gitignore`: `echo "tmp" >> .gitignore` +4. (Opcional) Usa el comando `tree` para comprobar que la estructura de + directorios es correcta. Al ejecutar `tree -a` en tu carpeta `devchallenge` + deberías ver una estructura como esta: + ```bash + devchallenge + ├── .gitignore + └── tmp + ├── state.json + └── output.json + ``` + +::: + +1. Crea un archivo de job llamado `hello.js` y escribe el siguiente código. + + ```js + console.log('Hello World!'); + ``` + +
+ ¿Qué es un job? + Un job de OpenFn es código JavaScript que sigue un conjunto concreto de convenciones. + Normalmente un job tiene una o más operaciones que realizan una tarea concreta + (como obtener información de una base de datos, crear un registro, etc.) y + devuelven el state para que lo use la siguiente operación. +
+ +
+ ¿Qué es console.log? + console.log es una función básica del lenguaje JavaScript que te + permite mostrar mensajes en la ventana de la terminal. +
+ +2. Ejecuta el job con la CLI + + ```bash + openfn hello.js -o tmp/output.json + ``` + +
+ Ver la salida esperada + + ```bash + [CLI] ⚠ WARNING: No adaptor provided! + [CLI] ⚠ This job will probably fail. Pass an adaptor with the -a flag, eg: + openfn job.js -a common + [CLI] ✔ Compiled from hello.js + [R/T] ♦ Starting job job-1 + [JOB] ℹ Hello World! + [R/T] ✔ Completed job job-1 in 1ms + [CLI] ✔ State written to tmp/output.json + [CLI] ✔ Finished in 17ms ✨ + + ``` + +
+ +Fíjate en que tu instrucción `console.log` se imprimió como +`[JOB] Hello World!`. Usar la consola así ayuda a depurar o a entender qué pasa +dentro de tus steps. + +### 2. Usar las funciones auxiliares de los adaptors {#2-using-adaptor-helper-functions} + +Los adaptors son módulos de JavaScript o +[TypeScript](https://www.typescriptlang.org/) (un superconjunto de JavaScript +con tipado fuerte) que ofrecen a los usuarios de OpenFn un conjunto de funciones +auxiliares para simplificar la comunicación con un sistema externo concreto. Más +información sobre los adaptors: [docs.openfn.org/adaptors](/adaptors/) + +#### Uso básico: {#basic-usage} + +Usemos el adaptor +[@openfn/language-http](https://www.npmjs.com/package/@openfn/language-http) +para obtener una lista de formularios de +[https://jsonplaceholder.typicode.com/](https://jsonplaceholder.typicode.com/) + +#### Tareas: {#tasks} + +1. Crea un archivo llamado `getPosts.js` y escribe el siguiente código + + ```jsx title=getPosts.js + get('https://jsonplaceholder.typicode.com/posts'); + fn(state => { + console.log(state.data[0]); + return state; + }); + ``` + +2. Ejecuta el job con este comando + +```bash +openfn getPosts.js -i -a http -o tmp/output.json +``` + +:::info Los argumentos de la CLI + +Usa `-a` para indicar el adaptor y `-i` para instalar automáticamente el adaptor +necesario + +Ejecuta `openfn help` para ver la lista completa de argumentos de la CLI. + +::: + +Como es la primera vez que usas el adaptor `http`, lo instalas con el argumento +`-i`. + +
+ 3. Expande para ver los logs esperados de la CLI + +```bash + [CLI] ✔ Installing packages... + [CLI] ✔ Installed @openfn/language-http@4.2.8 + [CLI] ✔ Installation complete in 14.555s + [CLI] ✔ Compiled from getPosts.js + [R/T] ♦ Starting job job-1 + GET request succeeded with 200 ✓ + [JOB] ℹ { + userId: 1, + id: 1, + title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', + body: 'quia et suscipit\n' + + 'suscipit recusandae consequuntur expedita et cum\n' + + 'reprehenderit molestiae ut ut quas totam\n' + + 'nostrum rerum est autem sunt rem eveniet architecto' + } + [R/T] ✔ Completed job job-1 in 872ms + [CLI] ✔ State written to tmp/output.json + [CLI] ✔ Finished in 15.518s ✨ + +``` + +
+ +:::warning Datos de ejemplo + +Los datos que aparecen en estos logs de la CLI provienen de la API de +[JSONPlaceholder](https://jsonplaceholder.typicode.com/) y no representan +información real. Solo sirven para pruebas y desarrollo. + +Para hacer pruebas precisas, considera usar datos reales de tu API o servicio. + +::: + +### 3. Entender `state` {#3-understanding-state} + +Si la expresión de un job es un conjunto de instrucciones para un chef (¿una +receta?), el state inicial son todos los ingredientes que necesita, bien atados +en un paquetito perfecto. Consulta +[It all starts with state](/articles/2021/07/05/wrapping-my-head-around-jobs/#it-all-starts-with-state) +en la base de conocimiento para más contexto. + +
+ Suele verse más o menos así + +```json +{ + "configuration": { + "hostUrl": "https://moh.kenya.gov.ke/dhis2", + "username": "someone", + "password": "something-secret" + }, + "data": { + "type": "registration", + "patient": { + "age": 24, + "gender": "M", + "nationalId": "321cs7" + } + } +} +``` + +
+ +#### `state.configuration` {#stateconfiguration} + +En esta clave van las credenciales que autorizan las conexiones con cualquier +sistema autenticado con el que interactúe el job. (Ten en cuenta que, cuando +usas la plataforma OpenFn en lugar de la CLI, esta parte de `state` suele +sobrescribirse en tiempo de ejecución con una "credencial" real). + +:::warning Importante + +Ten en cuenta que `console.log(state)` muestra todo el state, incluidos +elementos de `state.configuration` como el **nombre de usuario y la +contraseña**. Elimina este log cuando termines de depurar, para no exponer +información sensible por accidente cuando el job se despliegue en producción. + +La plataforma OpenFn tiene protecciones integradas para "limpiar" el state de +los logs, pero cuando usas la CLI directamente, ¡estás por tu cuenta! + +::: + +#### `state.data` {#statedata} + +En esta clave van los datos relacionados con un run concreto de un job. En la +plataforma, son los datos propios de la work order que vienen de una solicitud +HTTP que activa el trigger, o algún dato que se pasa de un job a otro. + +Con la CLI, `state.json` se carga automáticamente desde el directorio actual. + +También puedes indicar la ruta del archivo de state con la opción -s, +--state-path. + +Indica la ruta de tu archivo `state.json` con este comando: + +```bash +openfn hello.js -a http -s tmp/state.json -o tmp/output.json +``` + +
+ Expande para ver los logs esperados de la CLI + +``` +[CLI] ✔ Compiled job from hello.js +GET request succeeded with 200 ✓ +[R/T] ✔ Operation 1 complete in 876ms +[R/T] ✔ Operation 2 complete in 0ms +[CLI] ✔ Writing output to tmp/output.json +[CLI] ✔ Done in 1.222s! ✨ +``` + +
+ +#### ¿Cómo puedes usar el state? {#how-can-we-use-state} + +Cada adaptor tiene un esquema de configuración recomendado para tu `state.json`. +El +[esquema de configuración de http](/adaptors/packages/http-configuration-schema) +muestra cómo configurar `state.configuration` para `language-http`: + +```json +{ + "username": "name@email", + "password": "supersecret", + "baseUrl": "https://jsonplaceholder.typicode.com" +} +``` + +#### Tareas: {#tasks-1} + +1. Actualiza tu `state.json` para que quede así: + +
+ Expande para ver state.json + + ```json title=state.json + { + "configuration": { + "baseUrl": "https://jsonplaceholder.typicode.com" + } + } + ``` + +
+ +Como actualizaste la configuración en tu `state.json`, ahora puedes usar la +función auxiliar `get()` sin indicar la **baseUrl**, es decir, `get('posts')`. + +2. Actualiza tu job `getPosts.js` para que quede así: + +
+ Expande para ver getPosts.js + + ```js title="getPosts.js" + // Get all posts + get('posts'); + + fn(state => { + const posts = state.data; + console.log(posts[0]); + return state; + }); + ``` + +
+ +3. Ahora ejecuta el job con el siguiente comando + + ```bash + openfn getPosts.js -a http -s tmp/state.json -o tmp/output.json + ``` + +
+ Y comprueba que ves los logs esperados de la CLI: + + ```bash + [CLI] ✔ Compiled job from getPosts.js + GET request succeeded with 200 ✓ + [R/T] ✔ Operation 1 complete in 120ms + [JOB] ℹ { + userId: 1, + id: 1, + title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', + body: 'quia et suscipit\n' + + 'suscipit recusandae consequuntur expedita et cum\n' + + 'reprehenderit molestiae ut ut quas totam\n' + + 'nostrum rerum est autem sunt rem eveniet architecto' + } + [R/T] ✔ Operation 2 complete in 0ms + [CLI] ✔ Writing output to tmp/output.json + [CLI] ✔ Done in 470ms! ✨ + + ``` + +
+ +### 4. Limpiar y transformar datos {#4-clean--transform-data} + +En la mayoría de los casos necesitas manipular, limpiar o transformar datos en +algún step de tu workflow. Por ejemplo, después de obtener datos del registro +`https://jsonplaceholder.typicode.com`, quizás necesites agrupar las +publicaciones por id de usuario. El ejemplo de abajo muestra cómo: + +1. obtener todas las publicaciones y devolverlas en `state.data` +2. agrupar las publicaciones devueltas por `userId` +3. mostrar en el log las publicaciones con userId `1` + +
+Expande para ver el ejemplo: + +```js title="getPosts.js" +// Get all posts +get('posts'); + +// Group posts by user id +fn(state => { + const posts = state.data; + + // Group posts by userId + const groupPostsByUserId = posts.reduce((acc, post) => { + const existingValue = acc[post.userId] || []; + return { + ...acc, + [post.userId]: [...existingValue, post], + }; + }, {}); + + console.log(groupPostsByUserId); + + return { ...state, groupPostsByUserId }; +}); + +// Log posts where userId = 1 +fn(state => { + const { groupPostsByUserId } = state; + console.log('Post with userId 1', groupPostsByUserId[1]); + return state; +}); +``` + +
+ +
+¿Qué es array.reduce? +El método reduce() aplica una función a un acumulador y a cada +valor del array (de izquierda a derecha) para reducirlo a un único valor. + +Quizás el caso más fácil de entender de reduce() es devolver la +suma de todos los elementos de un array: + +##### Demostración de JavaScript: `Array.reduce()` {#javascript-demo-arrayreduce} + +``` + +// 0 + 1 + 2 + 3 + 4 +const array1 = [1, 2, 3, 4]; +const initialValue = 0; +const sumWithInitial = array1.reduce( + (accumulator, currentValue) => accumulator + currentValue, + initialValue +); + +console.log(sumWithInitial); // Expected output: 10 + +``` + +Puedes aprender más sobre `array.reduce` en la +[referencia de MDN de Array.prototype.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce). + +
+ +
+ Expande para ver los logs esperados de la CLI + +``` + +[CLI] ✔ Compiled job from getPosts.js +GET request succeeded with 200 ✓ +[R/T] ✔ Operation 1 complete in 825ms +[R/T] ✔ Operation 2 complete in 0ms +[JOB] ℹ Post with userId 1 [ //All of posts for userId 1 ] +[R/T] ✔ Operation 3 complete in 12ms +[CLI] ✔ Writing output to tmp/output.json +[CLI] ✔ Done in 1.239s! ✨ + +``` + +
+ +### 5. Depurar errores {#5-debugging-errors} + +Al depurar, es interesante y útil usar console.log para ver el contenido de los +objetos que manipulas (como state). + +Cuando quieras inspeccionar el contenido de state entre operaciones, agrega un +bloque `fn()` con un `console.log`: + +```js +// firstOperation(...); + +fn(state => { + console.log(state); + return state; +}); + +// secondOperation(...); +``` + +##### Crea **debug.js** y pega el código de abajo {#create-debugjs-and-paste-the-code-below} + +
+ + Expande para ver debug.js + +```jsx title="debug.js" +// Get all posts +get('posts'); + +// Get post by index helper function +fn(state => { + // const getPostbyIndex = (index) => dataValue(index)(state); + console.log(dataValue(1)); + + return { ...state }; +}); +``` + +
+ +##### Ejecuta **openfn debug.js -a http -s tmp/state.json** {#run-openfn-debugjs--a-http--s-tmpstatejson} + +
+ Logs esperados de la CLI + +```bash +[CLI] ✘ TypeError: path.match is not a function + at dataPath (/tmp/openfn/repo/node_modules/@openfn/language-common/dist/index.cjs:258:26) + at dataValue (/tmp/openfn/repo/node_modules/@openfn/language-common/dist/index.cjs:262:22) + at getPostbyIndex (vm:module(0):5:37) + at vm:module(0):18:36 + at /tmp/openfn/repo/node_modules/@openfn/language-common/dist/index.cjs:241:12 + at file:///home/openfn/.asdf/installs/nodejs/18.12.0/lib/node_modules/@openfn/cli/node_modules/@openfn/runtime/dist/index.js:288:26 + at process.processTicksAndRejections (node:internal/process/task_queues:95:5) + at async run (file:///home/openfn/.asdf/installs/nodejs/18.12.0/lib/node_modules/@openfn/cli/node_modules/@openfn/runtime/dist/index.js:269:18) + at async executeHandler (file:///home/openfn/.asdf/installs/nodejs/18.12.0/lib/node_modules/@openfn/cli/dist/process/runner.js:388:20) +``` + +
+ +Como ves en los logs, la función auxiliar `dataValue` tiene un TypeError. Para +solucionarlo, puedes ir a la documentación de **dataValue -> +[docs.openfn.org/adaptors/packages/common-docs/#datavalue](/adaptors/packages/common-docs/#datavalue)**. + +Según la documentación, dataValue recibe como entrada una ruta de tipo string. +Pero en tu operación pasabas un entero; por eso aparece el _TypeError_. Puedes +corregir el error pasando un string a dataValue, es decir, +`console.log(dataValue("1"))`. + +
+ Logs esperados de la CLI + +```bash +[CLI] ✔ Compiled job from debug.js +GET request succeeded with 200 ✓ +[R/T] ✔ Operation 1 complete in 722ms +[JOB] ℹ [Function (anonymous)] +[R/T] ✔ Operation 2 complete in 1ms +[CLI] ✔ Writing output to tmp/output.json +[CLI] ✔ Done in 1.102s ✨ +``` + +
+ +Si necesitas más información para depurar, puedes pasar `-l debug`. Esto +establece el nivel de log en _debug_, que registra toda la información de la +ejecución. + +Es decir, `openfn debug.js -a http -l debug`. + +### 6. Each e iteración de arrays {#6-each-and-array-iteration} + +A menudo tienes que realizar la misma operación varias veces, una por cada +elemento de un array. La mayoría de las funciones auxiliares para manipular +datos se heredan de @openfn/language-common y están disponibles en la mayoría de +los adaptors. + +##### Modifica getPosts.js para agrupar las publicaciones por ID de usuario {#modify-getpostsjs-to-group-posts-by-user-id} + +
+Expande para ver getPosts.js + +```js title="getPosts.js" +// Get all posts +get('posts'); + +// Group posts by user +fn(state => { + const posts = state.data; + + // Group posts by userId + const groupPostsByUserId = posts.reduce((acc, post) => { + const existingValue = acc[post.userId] || []; + return { ...acc, [post.userId]: [...existingValue, post] }; + }, {}); + + // console.log(groupPostsByUserId); + return { ...state, groupPostsByUserId }; +}); + +// Log posts where userId = 1 +fn(state => { + const { groupPostsByUserId } = state; + const posts = groupPostsByUserId[1]; + + // console.log("Post with userId 1", groupPostsByUserId[1]); + return { ...state, posts }; +}); + +each('posts[*]', state => { + console.log('Post', JSON.stringify(state.data, null, 2)); + return state; +}); +``` + +
+ +Fíjate en que este código usa la función `each`, una función auxiliar definida +en [language-common](/adaptors/packages/common-docs/#each) pero a la que se +accede desde este job, que usa `language-http`. La mayoría de los adaptors +importan muchas funciones de `language-common`. + +Ejecuta **openfn getPosts.js -a http -s tmp/state.json -o tmp/output.json** + +
+ Expande para ver los logs esperados de la CLI + +```bash +[CLI] ✔ Compiled job from getPosts.js +GET request succeeded with 200 ✓ +[R/T] ✔ Operation 1 complete in 730ms +[R/T] ✔ Operation 2 complete in 0ms +[R/T] ✔ Operation 3 complete in 0ms +[JOB] ℹ Posts [ +// Posts +] +[R/T] ✔ Operation 4 complete in 10ms +[CLI] ✔ Writing output to tmp/output.json +[CLI] ✔ Done in 1.091s! ✨ +``` + +
+ +### 7. Ejecutar workflows {#7-running-workflows} + +Ejecutar un workflow te permite definir una lista de steps y las reglas para +ejecutarlos. Puedes usar un workflow para orquestar el flujo de datos entre +sistemas de forma estructurada y automatizada. + +Por ejemplo, si tu workflow tiene dos steps (GET de usuarios del sistema A y +POST de usuarios al sistema B), puedes configurarlo para que ejecute todos los +steps en secuencia, de principio a fin. Esto imita los +[patrones de flow triggers](/documentation/legacy/build/triggers#flow-triggers) +de la plataforma OpenFn, donde un segundo job debería ejecutarse después de que +el primero termine con éxito, usando los datos que devolvió el primer job. + +:::info En resumen + +No tendrás que armar el state inicial del siguiente job: el state final del job +anterior se pasa automáticamente al job siguiente como state inicial. + +::: + +##### Workflow {#workflow} + +Un workflow es el plan de ejecución de varios steps en secuencia. Se define como +un objeto JSON con las siguientes propiedades: + +```json +{ + "options": { + "start": "a" // optionally specify the start node (defaults to steps[0]) + }, + "workflow": { + "steps": [ + { + "id": "a", + "expression": "fn((state) => state)", // code or a path + "adaptor": "@openfn/language-common@1.75", // specify the adaptor to use (version optional) + "state": { + "data": {} // optionally pre-populate the data object (this will be overridden by keys in previous state) + }, + "configuration": {}, // Use this to pass credentials + "next": { + // This object defines which steps to call next + // All edges returning true will run + // If there are no next edges, the workflow will end + "b": true, + "c": { + "condition": "!state.error" // Note that this is an expression, not a function + } + } + } + ] + } +} +``` + +###### Ejemplo de un workflow {#example-of-a-workflow} + +
+Este es un ejemplo de un workflow sencillo con tres steps: + +```json title="workflow.json" +{ + "options": { + "start": "getPatients" + }, + "workflow": { + "steps": [ + { + "id": "getPatients", + "adaptor": "http", + "expression": "getPatients.js", + "configuration": "tmp/http-creds.json", + "next": { + "getGlobalOrgUnits": true + } + }, + { + "id": "getGlobalOrgUnits", + "adaptor": "common", + "expression": "getGlobalOrgUnits.js", + "next": { + "createTEIs": true + } + }, + { + "id": "createTEIs", + "adaptor": "dhis2", + "expression": "createTEIs.js", + "configuration": "tmp/dhis2-creds.json" + } + ] + } +} +``` + +
+ +
+ tmp/http-creds.json + +```json title="tmp/http-creds.json" +{ + "baseUrl": "https://jsonplaceholder.typicode.com" +} +``` + +
+ +
+ tmp/dhis2-creds.json + +```json title="tmp/dhis2-creds.json" +{ + "hostUrl": "https://play.im.dhis2.org/dev", + "password": "district", + "username": "admin" +} +``` + +
+ +
+ getPatients.js + +```js title="getPatients.js" +// Get users from jsonplaceholder +get('users'); + +// Prepare new users as new patients +fn(state => { + const newPatients = state.data; + return { ...state, newPatients }; +}); +``` + +
+ +
+ getGlobalOrgUnits.js + +```js title="getGlobalOrgUnits.js" +// Globals: orgUnits +fn(state => { + const globalOrgUnits = [ + { + label: 'Njandama MCHP', + id: 'g8upMTyEZGZ', + source: 'Gwenborough', + }, + { + label: 'Njandama MCHP', + id: 'g8upMTyEZGZ', + source: 'Wisokyburgh', + }, + { + label: 'Njandama MCHP', + id: 'g8upMTyEZGZ', + source: 'McKenziehaven', + }, + { + label: 'Njandama MCHP', + id: 'g8upMTyEZGZ', + source: 'South Elvis', + }, + { + label: 'Ngelehun CHC', + id: 'IpHINAT79UW', + source: 'Roscoeview', + }, + { + label: 'Ngelehun CHC', + id: 'IpHINAT79UW', + source: 'South Christy', + }, + { + label: 'Ngelehun CHC', + id: 'IpHINAT79UW', + source: 'Howemouth', + }, + { + label: 'Ngelehun CHC', + id: 'IpHINAT79UW', + source: 'Aliyaview', + }, + { + label: 'Baoma Station CHP', + id: 'jNb63DIHuwU', + source: 'Bartholomebury', + }, + { + label: 'Baoma Station CHP', + id: 'jNb63DIHuwU', + source: 'Lebsackbury', + }, + ]; + + return { ...state, globalOrgUnits }; +}); +``` + +
+ +
+ createTEIs.js + +```js title="createTEIs.js" +fn(state => { + const { newPatients, globalOrgUnits } = state; + + const getOrgUnit = city => + globalOrgUnits.find(orgUnit => orgUnit.source === city).id; + + const mappedEntities = newPatients.map(patient => { + const [firstName = 'Patient', lastName = 'Test'] = ( + patient.name || '' + ).split(' '); + + const orgUnit = getOrgUnit(patient.address.city); + + const attributes = [ + { attribute: 'w75KJ2mc4zz', value: firstName }, + { attribute: 'zDhUuAYrxNC', value: lastName }, + { attribute: 'cejWyOfXge6', value: 'Male' }, + ]; + + return { ...patient, attributes: attributes, orgUnit: orgUnit }; + }); + + return { ...state, mappedEntities }; +}); + +each( + 'mappedEntities[*]', + create('trackedEntityInstances', { + orgUnit: dataValue('orgUnit'), + trackedEntityType: 'nEenWmSyUEp', + attributes: dataValue('attributes'), + }) +); +``` + +
+ +Para ejecutar el workflow, usa `openfn [path/to/workflow.json]`. + +
+ +Por ejemplo, si creaste workflow.json en la raíz del directorio de +tu proyecto, esta sería la estructura del proyecto: + + +```bash + devchallenge + ├── .gitignore + ├── getPatients.js + ├── createTEIs.js + ├── getGlobalOrgUnits.js + ├── workflow.json + └── tmp + ├── http-creds.json + ├── dhis2-creds.json + └── output.json +``` + +
+ +```bash +openfn workflow.json -o tmp/output.json +``` + +Al ejecutarse, este workflow empieza por el job `getPatients.js`. Si termina con +éxito, `getGlobalOrgUnits.js` se ejecuta con el state final de `getPatients.js`. +Si `getGlobalOrgUnits.js` termina con éxito, `createTEIs.js` se ejecuta con el +state final de `getGlobalOrgUnits.js`. + +Ten en cuenta que los adaptors indicados en `workflow.json` se instalan +automáticamente cuando ejecutas el workflow. Para ejecutar el workflow, usa este +comando: + +```bash +openfn workflow.json -o tmp/output.json +``` + +Al ejecutarlo, primero se instalan automáticamente los adaptors y luego se +ejecuta el workflow. + +:::danger Importante + +Cuando trabajes con el archivo `workflow.json`, es importante manejar de forma +segura la información sensible, como las credenciales y los datos de entrada +iniciales. Para proteger tus datos sensibles, sigue estas pautas: + +1. Clave de configuración: en el archivo `workflow.json`, indica la ruta a un + archivo de configuración ignorado por git que contenga las credenciales + necesarias para acceder al sistema de destino. Por ejemplo: + + ```json + { + ... + "configuration": "tmp/openMRS-credentials.json" + }, + ``` + +2. Clave de datos: si necesitas pasar datos iniciales a tu job, indica la ruta a + un archivo de datos ignorado por git: + ```json + { + ... + "state": { + "data": "tmp/initial-data.json", + } + } + ``` + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/security-for-devs.md b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/security-for-devs.md new file mode 100644 index 000000000000..75e190f2b60d --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build-for-developers/security-for-devs.md @@ -0,0 +1,198 @@ +--- +id: security-for-devs +title: Consideraciones de seguridad para el desarrollo con OpenFn +sidebar_label: Consideraciones de seguridad +slug: /security-for-devs +translation_source_hash: 82ba4f8c0b4cee7db4d2c18b5d0235b7d72436f6 +translation_review_status: machine +--- + +## Introducción {#introduction} + +Aunque las aplicaciones que integras sean seguras, las implementaciones de +integración de datos siguen teniendo muchos riesgos de seguridad. Sigue leyendo +para conocer buenas prácticas y consejos que te ayudarán a lograr la máxima +seguridad al crear workflows de OpenFn. + +## Errores comunes y cómo evitarlos {#common-mistakes-and-how-to-avoid-them} + +Escribir jobs de OpenFn puede ser sencillo, pero hay errores comunes que los +desarrolladores pueden cometer. Estos son algunos de ellos y consejos para +evitarlos: + +### Información sensible escrita en el código {#hardcoding-sensitive-information} + +- **Evita escribir credenciales en el código:** en lugar de escribir nombres de + usuario, contraseñas o claves de API directamente en tus scripts, usa el + objeto `state` para guardar y obtener la información sensible. + +```json +// Store credentials in state.json +{ + "configuration": { + "username": "your_username", + "password": "your_password" + } +} +``` + +```javascript +// Retrieve credentials from state +const username = state.configuration.username; +const password = state.configuration.password; +``` + +### Subir datos sensibles a GitHub {#checking-sensitive-data-into-github} + +- **Cuida el contenido del repositorio:** cuando trabajes en proyectos de + clientes, no guardes datos sensibles, como `state.json`, en el repositorio + público de GitHub del cliente. Revisa siempre el contenido del repositorio y + elimina u oculta la información sensible antes de hacer commit de tus cambios. + +- **Usa directorios específicos para los payloads de prueba:** si guardas + payloads de prueba en el repositorio de un cliente, usa directorios como + `sampleData/` y asegúrate de que los nombres de los archivos no se parezcan a + `state.json`. No incluyas objetos state completos en estos archivos; en su + lugar, reproduce exactamente el payload de prueba. + +- **Revisa el git diff antes de hacer commit:** antes de hacer un commit, lee + con atención el git diff línea por línea para detectar cambios y logs que no + querías incluir. Acostúmbrate a hacer commits selectivos + (`git add ./path/to/file`) en lugar de usar `git add -A`. + +- **Crea el hábito de revisar los diffs:** para crear el hábito, lee el primer + archivo en `git diff` y luego usa `git add ./that_file` si está listo. + Continúa revisando el diff y agregando archivos según sea necesario. Por + último, usa `git commit -m "my changes"` y haz push. + +### Uso de `console.log()` {#use-of-consolelog} + +- **Registra los logs con cuidado:** aunque `console.log()` es fundamental + durante el desarrollo de un job, es esencial tener cuidado de no registrar + información sensible. Una vez creados los jobs, los administradores pueden + desactivar la salida de los logs de consola, pero algunos aspectos del sandbox + podrían seguir mostrándose. + +- **Evita registrar datos sensibles:** ten cuidado al usar `console.log(state)` + o `console.log(state.configuration)`, sobre todo si contienen información + sensible como nombres de usuario y contraseñas. En algunos casos, esto puede + exponer datos sensibles en los logs. + +```javascript +// Good practice +console.log('Operation completed successfully.'); + +// Avoid logging sensitive data +console.log('Received sensitive data:', state.data); +``` + +- **Logs pensados para auditorías:** ten en cuenta que algunos clientes usan + `console.log()` con fines de auditoría, incluso en producción. Asegúrate de + que tu forma de registrar logs se ajuste a los requisitos del cliente y evita + exponer información confidencial sin querer. + +- **Control de los administradores:** ten en cuenta que los administradores + pueden controlar la visibilidad de los logs de consola en un proyecto de + OpenFn. Aun así, es fundamental registrar los logs de una forma que responda + tanto a las necesidades del desarrollo como a las consideraciones de seguridad + del cliente. + +> Estas buenas prácticas en el uso de `console.log()` contribuyen a un +> desarrollo seguro de jobs en OpenFn y reducen el riesgo de exponer datos +> sensibles en los logs sin querer. + +### No manejar los errores {#ignoring-error-handling} + +- **Maneja los errores de forma controlada:** implementa siempre el manejo de + errores en tus scripts. Incluye mensajes de error útiles en el objeto `state` + para facilitar la depuración sin exponer detalles sensibles en las respuestas. + +```javascript +// Set custom error message in state +state.error = new Error( + 'Authentication failed. Please check your credentials.' +); + +// Log the error in OpenFn +console.error(state.error); +``` + +### Permisos demasiado amplios en las credenciales {#overly-broad-permissions-on-credentials} + +Al agregar credenciales a OpenFn, es imprescindible seguir el principio de +mínimo privilegio. En concreto: + +- **Cuentas de usuario especializadas:** + + - **Usuario específico para el cliente:** crea una cuenta de usuario dedicada + para el sistema del cliente dentro de la aplicación de terceros (por + ejemplo, Salesforce). Esta cuenta de usuario debería usarse solo para la + integración con OpenFn. + + - **Acceso limitado a la API:** concede acceso a la API solo a los recursos + necesarios para las operaciones de OpenFn. Asegúrate de que los permisos se + limiten a los objetos y campos específicos que requiere el intercambio de + datos. + +- **Ámbitos de OAuth:** + + - **Control de ámbitos con OAuth:** en algunos casos, se usa OAuth para una + autenticación segura. Aprovecha las capacidades de OAuth para controlar los + ámbitos de acceso. Define y limita los permisos según los requisitos exactos + de OpenFn. + +- **Ejemplo de Salesforce:** + + - **Restricciones de objetos y campos:** en Salesforce, en concreto, crea una + cuenta de usuario con acceso a la API limitado a los objetos y campos + pertinentes. No concedas permisos innecesarios que podrían suponer riesgos + de seguridad. + + > Con estas prácticas, te aseguras de que las credenciales asociadas a + > OpenFn tengan permisos definidos y restringidos con precisión. Así se + > reduce el riesgo de accesos no deseados y se protegen los datos sensibles + > del cliente durante los procesos de integración. + +### Conservar tu proyecto de OpenFn {#retaining-your-openfn-project} + +Si tienes requisitos estrictos de residencia de datos, puedes configurar OpenFn +como un pipeline de datos de "retención cero" para garantizar el cumplimiento. +Así, no se conserva ningún dato procesado en los workflows de OpenFn (entradas y +salidas), ni siquiera en la oferta de la plataforma de OpenFn alojada en la +nube. + +A muchos usuarios les resulta útil conservar los datos en OpenFn de forma +temporal para resolver problemas. Por ejemplo, si tienes un workflow de 3 steps +y el workflow falla en el step 3, podría ser útil conservar la entrada de ese +step fallido para inspeccionar los datos, resolver el problema rápidamente y +volver a intentarlo desde ese punto. Para tener esta experiencia de resolución +de problemas más sencilla, la mayoría de los usuarios deja activada la retención +temporal de datos, y el superadministrador de OpenFn puede ajustar el periodo de +retención. + +### Prácticas de seguridad para adaptors {#adaptors-security-practices} + +- **Protección del state del cliente:** los adaptors (paquetes de lenguaje) + nunca deberían exponer directamente ninguna parte del state de un cliente. Los + callbacks pueden registrar partes del state, pero deberían evitar registrar la + configuración o los datos reales, ya que pueden contener información de + identificación personal (PII). + +- **Uso de metadatos:** los adaptors pueden basarse en metadatos sobre el state, + como registrar el "número de casos obtenidos" en una solicitud. Sin embargo, + está prohibido registrar la solicitud en sí. + +- **Proceso de revisión de seguridad:** a medida que más colaboradores + participan en los adaptors, las revisiones de seguridad rigurosas de las pull + requests pasan a ser fundamentales. Asegúrate de que se cumplan las prácticas + de seguridad descritas durante el proceso de revisión. + +:::tip Más información + +Para conocer más consideraciones de seguridad y buenas prácticas para todas las +personas que implementan OpenFn (no solo los desarrolladores), consulta la +[Guía de seguridad de OpenFn](/get-started/security.md) completa. Para saber más +sobre cómo escribir jobs, consulta la +[guía para escribir jobs](/jobs/job-writing-guide.md). + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/ai-assistant.md b/i18n/es/docusaurus-plugin-content-docs/current/build/ai-assistant.md new file mode 100644 index 000000000000..8b60540dc2bc --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/ai-assistant.md @@ -0,0 +1,165 @@ +--- +title: Usar el AI Assistant +sidebar_label: AI Assistant +translation_source_hash: 8f3412d45ba140eef764450fc214945e39981ba4 +translation_review_status: machine +--- + +El AI Assistant de OpenFn ofrece una interfaz de chat con un modelo de IA que te +ayuda a crear workflows. **Mira +[este video](https://www.youtube.com/watch?v=3L_cGl9tWRc&ab_channel=OpenFn.org) +para ver una introducción a cómo funciona.** + +Puedes usarlo para escribir borradores de código de jobs, revisarlo y depurarlo, +diagnosticar errores y entender lo que puede hacer la plataforma. + +:::info Crea workflows basados en IA en OpenFn + +Consulta los [adaptors](/adaptors) de OpenFn para crear workflows de OpenFn que +orquesten interacciones con LLM (como ChatGPT y Claude) y conviertan las +decisiones tomadas con IA en acciones y ejecución automatizada. + +::: + +

+ +

+ +:::caution ¿El Assistant no está disponible? ¿No lo encuentras? + +En las instalaciones locales de OpenFn, el administrador de la instancia tiene +que configurar el AI Assistant para que esté disponible. Consulta la +[documentación de despliegue](https://github.com/OpenFn/lightning/blob/main/DEPLOYMENT.md#ai-chat) +para obtener ayuda o contacta al superusuario de tu instancia. + +El Assistant está disponible en app.openfn.org, con créditos de uso según el +plan de tu proyecto. Consulta +[openfn.org/pricing](https://www.openfn.org/pricing) o contacta a +[support@openfn.org](mailto:support@openfn.org) para saber más sobre los planes +de pago de la plataforma de OpenFn alojada en la nube. + +::: + +## Sobre el Assistant {#about-the-assistant} + +El AI Assistant es un sistema multiagente personalizado. Tiene acceso a la +documentación de OpenFn y a buenas prácticas de implementación, así que puede +responder tus preguntas en el contexto de la plataforma. + +Todas las sesiones de chat se comparten entre todos los usuarios del proyecto. +Puedes empezar una sesión de chat nueva cuando quieras, o abrir una anterior. + +Puedes configurar si se envían al modelo el código de tu workflow, los logs de +los runs y los datos de entrada y salida. Compartir este contexto permite que el +Assistant dé una respuesta más pertinente, pero piensa bien si los datos son +confidenciales o sensibles antes de enviarlos. + +## Una nota sobre el uso responsable de la IA {#a-note-on-responsible-ai-usage} + +El AI Assistant usa modelos de lenguaje de gran tamaño (LLM). Como otros +chatbots, sus capacidades son impresionantes, pero imperfectas. + +Recuerda que, en definitiva, todas las respuestas se generan automáticamente y +TÚ, la persona a cargo, eres responsable de cómo se usa lo que produce. Deberías +analizar todas las respuestas con espíritu crítico y verificar el resultado +siempre que puedas. + +**Puedes leer más sobre nuestro enfoque de la IA en nuestra +[Política de IA responsable](https://www.openfn.org/ai).** + +## Cómo acceder al AI Assistant {#how-to-access-the-ai-assistant} + +Puedes acceder al AI Assistant desde el Canvas del workflow o desde un step de +job concreto, haciendo clic en el ícono de globo de diálogo de la esquina +superior derecha. + +Si ya hubo sesiones de chat anteriores, verás una lista con ellas. Haz clic en +una para abrir el historial de ese chat. + +Para empezar una sesión nueva, escribe una pregunta en el área de texto de la +parte inferior del Assistant. Haz clic en el botón `Send` para enviar tu +pregunta. El Assistant te devolverá una respuesta en la interfaz de chat. + +Puedes cerrar una sesión de chat haciendo clic en el botón `(X)` de la esquina +superior derecha de la interfaz de chat, que te lleva de vuelta a la lista de +sesiones. + +## Limpieza de datos {#data-scrubbing} + +Si decides enviar al Assistant los datos de entrada y salida de tus runs, el +Assistant recibe la forma de tus datos, no los datos en sí. Cada valor se +reemplaza por el tipo de dato que era. Los nombres de los campos se mantienen, +porque el Assistant los necesita para hablar de tus datos de forma útil. Ten en +cuenta que los logs de los runs y el código no se limpian. + +Así que, si un step se ejecutó con esto: + +```json +{ + "patient": { + "name": "Amina Yusuf", + "dob": "2000-01-01", + "phone": "+123456789", + "visits": 3, + "consented": true, + "notes": null + }, + "records": [ + { "id": "R-001", "weight": 61.5 }, + { "id": "R-002", "weight": 58.0 }, + { "id": "R-003", "weight": 70.2 }, + { "id": "R-004", "weight": 64.1 } + ] +} +``` + +esto es lo que recibe el Assistant: + +```json +{ + "patient": { + "consented": "boolean", + "dob": "string", + "name": "string", + "notes": "null", + "phone": "string", + "visits": "number" + }, + "records": [ + { "id": "string", "weight": "number" }, + { "id": "string", "weight": "number" }, + "...2 more" + ] +} +``` + +El nombre, la fecha de nacimiento y el número de teléfono nunca salen de OpenFn. +El Assistant puede ver igualmente que hay un paciente con un número de teléfono +y cuatro registros, que suele ser todo lo que necesita para ayudarte a corregir +el código de tu job. + +### Otras cosas que hace {#a-few-other-things-it-does} + +- Las listas largas se recortan a dos ejemplos. El resto se cuenta, como + `"...2 more"` en el ejemplo de arriba. +- Los registros muy anchos se recortan a 50 campos. El resto se cuenta en un + campo `"..."`. +- Si la política de retención de tu proyecto ya borró los datos de un step, al + Assistant se le envía `[erased by this project's retention policy]` en su + lugar. Se le avisa cuando se omitieron datos, para que no dé por hecho que lo + vio todo. +- Los datos muy grandes no se envían. Verás `[too large to summarise]`. + +### Lo único que tienes que saber {#the-one-thing-to-know} + +Los nombres de los campos se envían tal cual. Si tus datos usan el nombre de una +persona o un número de documento nacional de identidad como nombre de campo, ese +nombre o número se enviará. Los valores están protegidos; las claves, no. + +:::caution ¿Comentarios o preguntas sobre el Assistant? + +Tus preguntas y comentarios son bienvenidos en +[community.openfn.org](https://community.openfn.org/), o contacta a +[support@openfn.org](mailto:support@openfn.org) para consultas privadas. + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/channels.md b/i18n/es/docusaurus-plugin-content-docs/current/build/channels.md new file mode 100644 index 000000000000..391f32fa2341 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/channels.md @@ -0,0 +1,263 @@ +--- +title: Canales +sidebar_label: Canales +translation_source_hash: 5d829d4c4243d09a5e89ee9020f2bd22c3a73921 +translation_review_status: machine +--- + +Los canales convierten OpenFn en un **proxy inverso**: un intermediario seguro +entre dos sistemas. En lugar de conectar una aplicación cliente directamente a +un servicio de destino, el cliente envía sus solicitudes a OpenFn, y OpenFn las +reenvía, se encarga de la autenticación y registra cada solicitud en el camino. + +``` +Mobile App → OpenFn Channel → Health Registry +(client) (the middleman) (destination) +``` + +Obtienes visibilidad inmediata de todo lo que pasa por el canal, sin escribir +código de workflows. A diferencia de los workflows, los canales son un simple +_paso directo_: OpenFn no transforma los datos, sino que los enruta, los protege +y los observa. + +Los canales están pensados para organizaciones que necesitan una capa de proxy +para la seguridad, la observabilidad y el control de acceso en sus intercambios +de datos, como los intercambios de información de salud que históricamente han +usado una herramienta independiente como OpenHIM para este fin. Los canales +ofrecen esa funcionalidad básica de proxy inverso de forma nativa dentro de +OpenFn. + +:::tip Funcionalidad experimental + +Los canales son actualmente una funcionalidad experimental. Para usarlos, activa +**Experimental Features** en la página de tu +[perfil de usuario](/manage-users/user-profile.md). Si no ves el elemento +`Channels` en la barra lateral de tu proyecto, el motivo es esta opción. + +::: + +## Cómo funciona {#how-it-works} + +Cuando un cliente envía una solicitud HTTP a la URL de proxy de tu canal, +OpenFn: + +1. Recibe la solicitud en `/channels/{channel-id}/{path}` +2. Busca el canal y comprueba que esté activado +3. Autentica al cliente, si hay credenciales de cliente configuradas +4. Reenvía la solicitud a `{destination-url}/{path}`, conservando el método, el + cuerpo, los encabezados y los parámetros de consulta +5. Agrega los encabezados `x-forwarded-for`, `x-forwarded-host`, + `x-forwarded-proto` y `x-request-id` para que el destino pueda rastrear la + solicitud +6. Adjunta un encabezado `Authorization` para el destino, si hay una credencial + de destino configurada +7. Devuelve la respuesta del destino directamente al cliente, en streaming +8. Registra la solicitud en `History` → `Channel Logs` + +Se admiten todos los métodos HTTP estándar: `GET`, `POST`, `PUT`, `PATCH`, +`DELETE` y otros. + +Por seguridad, OpenFn nunca reenvía cookies al destino, y las credenciales que +el cliente usó para autenticarse _ante OpenFn_ (el encabezado `Authorization` +para Basic Auth, o el encabezado `x-api-key` para las claves de API) se eliminan +antes de pasar la solicitud. + +## Antes de empezar {#before-you-start} + +Necesitas: + +- La opción **Experimental Features** activada en tu perfil de usuario +- Un **proyecto** en el que tengas el + [rol](/manage-projects/user-roles-permissions.md) `Owner`, `Admin` o `Editor` + (los usuarios con rol Viewer pueden ver los canales y sus logs, pero no pueden + crearlos ni modificarlos) +- La **URL del servicio de destino** al que quieres hacer de proxy (una API + pública como `https://hacker-news.firebaseio.com/v0` funciona muy bien para + hacer pruebas) + +## Paso 1: configura las credenciales (opcional) {#step-1-set-up-credentials-optional} + +Los canales usan dos tipos de credenciales, y ambos son opcionales: + +- Las **credenciales de cliente** controlan quién puede enviar solicitudes _a tu + canal_. Son los mismos + [métodos de autenticación de webhooks](/manage-projects/webhook-auth.md) que + se usan para proteger los triggers webhook (Basic HTTP Authentication o API + Key Authentication) y se gestionan en `Webhook Security`, en la configuración + de tu proyecto. +- Una **credencial de destino** es la forma en que OpenFn se autentica _ante el + servicio de destino_. Es una [credencial de proyecto](/build/credentials.md) + normal, y OpenFn la usa para construir el encabezado `Authorization` en cada + solicitud reenviada. Actualmente, los canales admiten estos tipos de + credenciales: + +| Tipo de credencial | Encabezado que se envía al destino | +| ------------------ | --------------------------------------------------- | +| HTTP | Token `Bearer`, o Basic Auth (usuario y contraseña) | +| DHIS2 | `ApiToken`, o Basic Auth (usuario y contraseña) | +| OAuth | Token `Bearer`, que OpenFn renueva automáticamente | + +:::tip + +Si solo quieres hacer pruebas con un endpoint público, sáltate este paso por +completo: no necesitas credenciales. + +::: + +## Paso 2: crea un canal {#step-2-create-a-channel} + +1. Ve a tu proyecto +2. Haz clic en `Channels` en la barra lateral izquierda +3. Haz clic en `New Channel` +4. Completa el formulario: + +| Campo | Qué poner | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Name | Un nombre para identificar este canal (debe ser único dentro del proyecto) | +| Enabled | Debe estar activado para que el canal acepte solicitudes | +| Destination URL | La URL base del servicio al que OpenFn reenviará las solicitudes | +| Destination Credential | Cómo se autentica OpenFn ante el servicio de destino (déjalo en `None` si el destino es público) | +| Client Credentials | Marca los métodos de autenticación de webhooks que los clientes pueden usar para acceder a este canal (déjalos sin marcar para permitir solicitudes sin autenticar durante las pruebas) | + +5. Haz clic en `Save` + +Una vez guardado, tu canal aparece en la lista de canales con su **URL de +proxy**. Haz clic en la URL para copiarla al portapapeles. Tiene este aspecto: + +``` +https://your-openfn-instance.com/channels/{channel-id} +``` + +## Paso 3: envía una solicitud a través del canal {#step-3-send-a-request-through-the-channel} + +Envía una solicitud HTTP a la URL de proxy de tu canal y agrega al final la ruta +del destino a la que quieras llegar: + +``` +https://your-openfn-instance.com/channels/{channel-id}/{path} +``` + +OpenFn la reenvía a `{destination-url}/{path}`. + +### Ejemplo con curl {#example-using-curl} + +Supongamos que la URL de destino de tu canal es +`https://hacker-news.firebaseio.com/v0`. Para obtener una noticia de Hacker News +a través de tu canal: + +```bash +curl https://app.openfn.org/channels/{channel-id}/item/8863.json +``` + +OpenFn recibe la solicitud, la reenvía a +`https://hacker-news.firebaseio.com/v0/item/8863.json` y te devuelve la +respuesta. + +### Más ejemplos {#more-examples} + +**POST con un cuerpo:** + +```bash +curl -X POST https://app.openfn.org/channels/{channel-id}/patients \ + -H "Content-Type: application/json" \ + -d '{"patient_id": "123", "status": "admitted"}' +``` + +**Con parámetros de consulta:** + +```bash +curl "https://app.openfn.org/channels/{channel-id}/patients?status=admitted" +``` + +**Con una credencial de cliente** (si configuraste una): + +```bash +# Basic Auth +curl -u username:password https://app.openfn.org/channels/{channel-id}/patients + +# API key +curl -H "x-api-key: your-api-key" https://app.openfn.org/channels/{channel-id}/patients +``` + +## Paso 4: consulta los logs {#step-4-view-the-logs} + +Cada solicitud que pasa por un canal queda registrada. + +1. Ve a la página `History` de tu proyecto +2. Haz clic en la pestaña `Channel Logs` +3. Verás cada solicitud en la lista, con su Request ID, la ruta de la solicitud, + el nombre del canal, la hora de inicio, el estado y el mensaje de error, si + lo hay + +Haz clic en una solicitud para abrir su página de detalle completa, que muestra +los encabezados y una vista previa del cuerpo de la solicitud y de la respuesta, +la información de tiempos y la configuración que tenía el canal cuando se hizo +la solicitud. Los encabezados sensibles (como `Authorization`) aparecen ocultos +en los logs. + +Cada solicitud tiene uno de estos estados: + +| Estado | Significado | +| ------- | ------------------------------------------------------------------------- | +| Pending | La solicitud todavía está en curso | +| Success | El destino respondió con un código de estado `2xx` | +| Failed | El destino respondió con un código de estado `4xx` o `5xx` | +| Timeout | El destino no respondió a tiempo | +| Error | No se pudo completar la solicitud (por ejemplo, por un error de conexión) | + +Si tienes varios canales, usa el filtro **Channel** para limitar la lista a un +solo canal. También puedes ir directamente a los logs filtrados de un canal +haciendo clic en su cantidad de **Requests** o en su **Last Activity** en la +página Channels. + +:::info + +Que se guarden o no los payloads de las solicitudes y las respuestas depende de +la configuración de [Data Storage](/manage-projects/io-data-storage.md) de tu +proyecto. Si tu proyecto no guarda los datos de entrada y salida, los metadatos +de las solicitudes del canal se siguen registrando, pero los payloads se borran. + +::: + +## Consideraciones de seguridad {#security-considerations} + +- El endpoint del proxy es **accesible públicamente**. Si no hay credenciales de + cliente configuradas, cualquiera que conozca la URL del canal puede enviar + solicitudes a través de él. Configura siempre credenciales de cliente para los + canales de producción. +- Al cambiar un canal a **deshabilitado**, deja de aceptar solicitudes de + inmediato (los clientes reciben un `404`). +- Cada solicitud se registra junto con una instantánea de la configuración que + tenía el canal en ese momento, así que tienes un registro de auditoría incluso + después de que el canal cambie. +- Un canal con historial de solicitudes no se puede eliminar, porque hay que + conservar su historial; deshabilítalo en su lugar. + +## Limitaciones {#limitations} + +- Los canales solo hacen de proxy para tráfico HTTP(S); no se admite el paso + directo de TCP sin procesar ni de TLS +- La ruta de la solicitud se reenvía tal cual; no se admite transformar la ruta +- No se admite la auditoría ATNA + +## Solución de problemas {#troubleshooting} + +| Problema | Causa probable | Solución | +| ----------------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| `404` en la URL del canal | El canal está deshabilitado, o el ID del canal es incorrecto | Comprueba que el canal esté habilitado y que el ID coincida | +| `401` en la URL del canal | Hay credenciales de cliente configuradas y tu solicitud no coincide con ninguna | Envía las credenciales correctas con tu solicitud, o desmarca las credenciales de cliente para hacer pruebas | +| `502` en la URL del canal | OpenFn no pudo usar la credencial de destino (por ejemplo, hay que volver a autorizarla) | Revisa la credencial de destino y el mensaje de error en `Channel Logs` | +| La solicitud pasa, pero el destino devuelve un error | La URL de destino o la ruta es incorrecta | Revisa bien la URL de destino y la ruta que agregas al final | +| No aparece el elemento `Channels` en la barra lateral | La opción Experimental Features está desactivada | Habilita `Experimental Features` en tu perfil de usuario | + +## Referencia rápida {#quick-reference} + +| Qué | Dónde encontrarlo | +| ----------------------------- | ----------------------------------------------------------------- | +| Crear y gestionar canales | `Project` → `Channels` | +| URL de proxy | Haz clic para copiarla desde la lista de canales, o abre el canal | +| Patrón del endpoint del proxy | `https://{instance}/channels/{channel-id}/{path}` | +| Ver los logs | `Project` → `History` → pestaña `Channel Logs` | +| Logs filtrados de un canal | Haz clic en la cantidad de `Requests` en la página Channels | +| Credenciales de cliente | `Project Settings` → `Webhook Security` | +| Credenciales de destino | `Project Settings` → `Credentials` | diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/collections.md b/i18n/es/docusaurus-plugin-content-docs/current/build/collections.md new file mode 100644 index 000000000000..eda2c841024e --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/collections.md @@ -0,0 +1,207 @@ +--- +title: Colecciones +sidebar_label: Colecciones +translation_source_hash: bdf77a9b1e55431a03704daec4224dda3b396dbf +translation_review_status: machine +--- + +Las colecciones son una solución de almacenamiento de gran volumen y alto +rendimiento integrada en OpenFn. Mira +**[este video](https://www.youtube.com/watch?v=iXkkkzratzY&t=3s&ab_channel=OpenFn.org)** +para una introducción. + +Las colecciones sirven para almacenar en búfer, guardar en caché y agregar datos +de webhooks, guardar archivos de mapeo grandes y compartir state entre +workflows. + +En las colecciones se puede guardar una cantidad muy grande de elementos (del +orden de millones). + +## Casos de uso {#use-cases} + +### Almacenar datos en búfer {#buffering-data} + +Muchas integraciones de OpenFn se disparan con un webhook, al que llama otro +sistema a partir de algún evento. Por ejemplo, cada vez que se registra un +paciente, un webhook llama a OpenFn para disparar un workflow y propagar el +evento de registro a otros sistemas. + +Las colecciones pueden servir de búfer para estos eventos entrantes: guardan los +datos del evento en OpenFn para procesar después un lote de eventos al final del +día. Esto es especialmente útil con eventos de gran volumen, o cuando los +sistemas de origen tienen límites. + +Con las colecciones, puedes guardar cada evento entrante en OpenFn y luego +ejecutar un workflow con un trigger cron que procese un lote de eventos de una +sola vez y envíe los resultados agregados, filtrados o transformados al +siguiente sistema. + +### Estructuras de mapeo {#mapping-structures} + +Un caso de uso típico en las integraciones de datos es almacenar objetos de +mapeo grandes. Estos objetos son pares clave-valor que asignan cadenas de un +sistema a las cadenas correspondientes de otro sistema. Por ejemplo, mapear +códigos médicos a SNOMED, mapear códigos de ciudad a cadenas legibles para las +personas o mapear una cadena de entrada a un código de atributo de DHIS2. + +Estos objetos suelen ser muy grandes y difíciles de mantener, y pueden inflar el +código del job. + +En cambio, los mapeos se pueden guardar como un objeto JSON en un repositorio de +GitHub y subirse a una colección con la CLI. + +## Conceptos básicos de las colecciones {#collections-basics} + +:::tip + +La API de colecciones está disponible automáticamente para todos los workflows y +no necesita credenciales. La autenticación con la plataforma OpenFn se gestiona +por ti. + +Puedes usar la API de colecciones con cualquier adaptor. + +::: + +Los datos se guardan como pares clave-valor, donde la clave es un identificador +único de ciertos datos (como un UUID o una marca de tiempo). El valor siempre se +guarda como cadena (aunque puedes pasar directamente objetos compatibles con +JSON, que la API de colecciones serializa automáticamente). + +Las claves se pueden obtener en bloque y filtrar por _patrón_. Por ejemplo, el +patrón `2024*` coincide con todas las claves que empiezan con `2024`. En las +aplicaciones de colecciones de gran volumen, es fundamental diseñar las claves +para que tengan un orden de clasificación eficiente. + +El siguiente ejemplo obtiene valores de la colección +`openfn-patient-registrations` y los guarda en el state para procesarlos +después: + +```js +collections.get('openfn-patient-registrations', '2024*').then(state => { + state.registrationsThisYear = state.data; + return state; +}); +``` + +Los elementos devueltos se escriben en state.data como un array de pares +`[{ key, value }]`: + +```js +{ + "data": { + "20240102-5901257": { + "name": "Tom Waits", + "id": "5901257", + }, + "20240213-0183216": { + "name": "Billie Holiday", + "id": "0183216", + } + } +} +``` + +Si obtienes un solo elemento (es decir, sin `*` en la clave), se escribe +directamente en `state.data`, sin clave: + +```js +{ + "data": { + "name": "Billie Holiday", + "id": "0183216", + } +} +``` + +Cada clave guarda de forma permanente su fecha de creación, así que, además de +obtenerlas por patrón de clave, también puedes filtrar las claves por fecha. +Este ejemplo obtiene todas las claves creadas antes del 30 de septiembre de +2024: + +```js +collections + .get('openfn-patient-registrations', '*', { createdBefore: '2024-09-30' }) + .then(state => { + state.registrationsThisYear = state.data; + return state; + }); +``` + +`collections.get` descarga en memoria todos los valores que coinciden. Para +valores grandes o conjuntos de valores de gran volumen, es más eficiente usar +`collections.each`, que carga cada valor en memoria por separado, en streaming, +y luego lo descarta. + +```js +collections.each( + 'my-collection', + { key: '2024*', createdAfter: '20240601' }, + (state, value, key) => { + console.log(value); + } +); +``` + +Los valores se suben a una colección con `collections.set`. Todas las +asignaciones son "upserts": se crean claves nuevas para los valores que no +existen y se actualizan los valores de las claves que _sí_ existen. + +El siguiente ejemplo asigna un solo elemento: + +```js +collections.set('openfn-demo', 'commcare-fhir-value-mappings', { + current_smoker: { + system: 'http://snomed.info/sct', + code: '77176002', + display: 'Smoker', + }, + /* ... */ +}); +``` + +Si asignas varios valores a la vez, pasa una función generadora de claves en +lugar de un id, para generar una clave para cada elemento. Por ejemplo, si se +guardan varios valores en un array en `state.data`: + +```js +collections.set('openfn-demo', (patient, state) => patient.id, $.patients); +``` + +La función generadora de claves se llama con cada valor y debe devolver una +clave de tipo cadena. + +## Gestionar colecciones {#managing-collections} + +Las colecciones se pueden crear, eliminar o renombrar desde el menú Admin. + +![Página de administración de colecciones](/img/collections_admin.webp) + +Antes de poder usar una colección, hay que crearla. Los nombres de las +colecciones deben ser únicos en el despliegue, así que recomendamos usar como +prefijo tu organización (y quizás el proyecto), por ejemplo, `openfn-demo`. + +## Usar colecciones {#using-collections} + +Las colecciones están disponibles para todos los workflows mediante una interfaz +sencilla de alto nivel. + +:::caution + +Hay que crear una colección en la interfaz de administración antes de poder +usarla. + +::: + +La API de colecciones ofrece cuatro verbos básicos: + +- [`collections.get()`](/adaptors/packages/collections-docs#collections_get) + descarga los valores que coinciden con una clave o un patrón de clave. +- [`collections.each()`](/adaptors/packages/collections-docs#collections_each) + recorre de forma eficiente un rango de elementos de una colección. +- [`collections.set()`](/adaptors/packages/collections-docs#collections_set) + sube valores a una colección. +- [`collections.remove()`](/adaptors/packages/collections-docs#collections_remove) + elimina valores por clave o patrón de clave. + +La API de colecciones se apoya en un adaptor especial: consulta la +[API del adaptor de colecciones](/adaptors/collections) para más detalles. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/credentials.md b/i18n/es/docusaurus-plugin-content-docs/current/build/credentials.md new file mode 100644 index 000000000000..1020914ff4ea --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/credentials.md @@ -0,0 +1,142 @@ +--- +title: Credenciales +translation_source_hash: 5b3d2b9a4a5e7e4ec6cae6b4870ae4a718dfc5fb +translation_review_status: machine +--- + +Las credenciales sirven para autorizar conexiones con sistemas externos. Algunos +adaptors usan credenciales para obtener metadatos de las aplicaciones de origen +y de destino, lo que facilita escribir jobs. + +Los valores de una credencial solo los puede ver o editar un único usuario: su +"propietario" (el usuario que creó esa credencial). Todos los colaboradores de +un proyecto pueden elegir entre todas las credenciales del proyecto al definir +un job. + +![Página de credenciales](/img/settings_credentials.webp) + +### Crear una credencial nueva {#create-a-new-credential} + +Puedes crear una credencial nueva mientras configuras un step nuevo en tu +workflow, o desde la página Settings > Credentials. +[Lee esta guía](/manage-projects/manage-credentials.md) para saber más sobre +cómo gestionar credenciales. + +### Entender las credenciales de cada aplicación {#understand-the-app-specific-credentials} + +Consulta la página de [documentación de adaptors](/adaptors) dedicada a tu +aplicación para revisar el `configuration schema` y ver qué datos de la +credencial se necesitan para autenticarte en tu aplicación (por ejemplo, +`username`, `api_key`). + +Si tu aplicación no aparece en la sección de adaptors, revisa la documentación +de su API para ver qué se necesita para la "autenticación". Luego puedes crear +en OpenFn una credencial `Raw JSON` para definir los datos de credencial que +hagan falta. Por ejemplo: + +```json +{ + "apiKey": "someSecretKey", + "baseUrl": "https://example.com/api/v2" +} +``` + +Ten en cuenta que algunos sistemas (Salesforce, OpenMRS, DHIS2) requieren un +instanceUrl, host o ApiUrl. Aunque la mayoría de los adaptors manejan bien una +"barra final" en una URL, si tienes dudas, es mejor no ponerla. Por ejemplo: + +- prefiere `https://login.salesforce.com` a `https://login.salesforce.com/`, +- usa `http://demo.openmrs.org/openmrs` en lugar de + `http://demo.openmrs.org/openmrs/`, +- y escribe `https://play.dhis2.org` en vez de `https://play.dhis2.org/`. + +### Usar credenciales OAuth2 {#use-oauth2-credentials} + +Si en tu instancia de OpenFn se configuraron _clientes_ OAuth2, puedes usarlos +para crear credenciales OAuth: + +1. Primero, elige un tipo de credencial OAuth en la interfaz "New Credential". +2. Luego, ponle un nombre. +3. Opcionalmente, selecciona los "scopes" adicionales que quieras usar. (Según + la aplicación que uses, consulta el enlace a la documentación del tercero + sobre scopes que aparece en la aplicación). +4. Por último, haz clic en "Sign in with \_\_\_\_\_\_". + +La aplicación del tercero te pedirá que verifiques tu identidad y que confirmes +que quieres usar OAuth. Cuando aceptes, de vuelta en OpenFn podrás guardar y +usar tu nueva credencial como cualquier otra. + +:::tip + +Si usas una instancia desplegada de OpenFn y no encuentras el tipo de credencial +OAuth para tu aplicación, contacta al superusuario responsable de configurar tu +instancia y pídele que configure clientes OAuth para tu aplicación. Si usas la +plataforma SaaS alojada de OpenFn, puedes publicar en +[community.openfn.org](https://community.openfn.org) o escribir a +[support@openfn.org](mailto:support@openfn.org). + +::: + +#### Por ejemplo: credencial OAuth de Google Sheets {#eg-googlesheets-oauth-credential} + +Observa que la credencial selecciona solo los scopes necesarios para Google +Sheets. + +![Credencial OAuth de Google Sheets](/img/gsheets-oauth2.webp) + +#### Por ejemplo: credencial OAuth de Salesforce {#eg-salesforce-oauth-credential} + +Observa que puedes elegir a qué scopes acceder en Salesforce. + +![Credencial OAuth de Salesforce](/img/salesforce-oauth2.webp) + +:::tip + +Consulta la página de [documentación de adaptors](/adaptors) de tu aplicación +para ver indicaciones sobre sus credenciales. + +::: + +### Crear un "usuario de integración" dedicado para tu workflow de OpenFn {#creating-a-dedicated-integration-user-for-your-openfn-workflow} + +Para que los sistemas de destino sean lo más seguros y controlados posible, +recomendamos que las credenciales que se usan en la integración tengan acceso +solo por API a la aplicación de destino. + +_Puedes_ usar tu usuario personal como credencial de OpenFn para tu workflow, +pero recomendamos crear un usuario de integración "OpenFn" dedicado o un usuario +de cuenta de servicio para acceder a tus aplicaciones de destino. Por ejemplo, +en [Salesforce](/adaptors/salesforce#salesforce-credentials) puedes crear un +usuario solo de API con un tipo de licencia especial solo de API para realizar +tareas automatizadas e integraciones sin necesitar acceso completo de usuario. +Para las API de Google, como +[Google Sheets](/adaptors/googlesheets#using-a-google-service-account), lo +recomendable para los workflows automatizados es una cuenta de servicio de +Google. + +Puede que no todos los sistemas de destino ofrezcan usuarios solo de API, pero +muchos permiten crear roles de usuario con permisos de acceso solo por API, y es +posible que te dejen definir a qué API o endpoints pueden acceder los usuarios. +Incluso cuando el sistema de destino no ofrece un usuario solo de API, las +buenas prácticas indican usar un usuario de integración o de servicio para todas +las tareas de automatización, para mantener un registro de auditoría seguro. + +El acceso solo por API reduce el riesgo de filtraciones de datos porque: + +- **Garantiza la trazabilidad**: acceder con un usuario de integración deja un + registro de auditoría de quién inició sesión, cuándo y qué cambios hizo. Por + ejemplo, si usaras tu usuario personal para un sistema en una implementación + de integración, sería difícil saber si fuiste TÚ, una persona, quien hizo un + cambio, o si fue una acción automatizada del sistema a través del usuario de + API. + +- **Minimiza el impacto de una filtración**: si el usuario se ve comprometido, + se puede desactivar, y el inicio de sesión en el frontend con la credencial de + API filtrada se bloquea automáticamente, lo que limita los vectores de ataque. + +- **Aplica el principio de mínimo privilegio**: cada usuario de integración solo + necesita acceso al subconjunto de datos que requiere su caso de uso + específico. + +Consulta la documentación sobre +[buenas prácticas de seguridad](/get-started/security.md) para saber más. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/editing-locally.md b/i18n/es/docusaurus-plugin-content-docs/current/build/editing-locally.md new file mode 100644 index 000000000000..312dfe07fa99 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/editing-locally.md @@ -0,0 +1,79 @@ +--- +title: Editar steps localmente +sidebar_label: Editar steps localmente +translation_source_hash: 17d7dee86170ea20aa87a3e41929b8bcded068d2 +translation_review_status: machine +--- + +Si eres desarrollador, puedes usar tu editor de texto favorito y hacer cambios +sin conexión, con commits y push a GitHub. Esta página explica cómo editar steps +localmente, en lugar de hacerlo en la plataforma con +[el Inspector](/build/steps/step-editor.md). + +Primero, asegúrate de que el control de versiones esté configurado para tu +proyecto (consulta [Gestionar proyectos](/manage-projects/platform-mgmt.md) para +saber cómo configurarlo). Cuando esté listo, sigue estos pasos en tu +computadora: + +1. Asegúrate de tener + [git instalado](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) + +2. Clona el repositorio desde GitHub. Según cómo te conectes, copia la URL HTTPS + o SSH del repositorio. + +![URL para clonar en GitHub](/img/git_clone_url.webp) + +:::tip + +Puedes conectarte a GitHub con usuario y contraseña (HTTPS) o con un par de +claves SSH que hayas generado. (Consulta la +[documentación de GitHub](https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories) +para más información). + +::: + +3. Luego, úsala para + [clonar el repositorio](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) + en tu computadora; para ello, ejecuta este comando en la carpeta donde + quieras guardar el nuevo repositorio: `git clone {repo URL}` (por ejemplo, + `git clone https://github.com/OpenFn/Miracle-Feet.git`) + +4. Para actualizar tu copia local con los cambios de GitHub, ejecuta `git pull` + con frecuencia mientras editas. + +5. En este tutorial, suponemos que haces los cambios en la rama `main` o + `master`: la que está desplegada en OpenFn como tu sistema de producción. + +6. Para editar tus steps, usa un editor de código. Recomendamos + [Visual Studio Code](https://code.visualstudio.com/download). + +![VS Code](/img/edit_job_vscode.webp) + +7. Si usas VS Code, asegúrate de instalar la + [extensión Prettier para VS Code](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) + y configurarla como formateador predeterminado en Settings, como se ve abajo. + Así se aplica el formato de código correcto a los archivos que cambies. + +![Prettier](/img/prettier.webp) + +8. Cuando termines, puedes ver qué archivos cambiaste con `git status`. + +9. Luego, usa `git add {filepath}` y después `git commit -m {change notes}` para + preparar los cambios para hacer merge en el repositorio. + +:::tip + +Hay mucho que aprender sobre git. +[Este es un buen punto de partida](https://github.com/git-guides/git-commit). + +::: + +10. Luego, ejecuta `git push` para subir los archivos al repositorio (consulta + más en la [documentación de git](https://github.com/git-guides/git-push)). + +A partir de ahí, la integración con el control de versiones actualizará los +steps modificados en tu proyecto de OpenFn y podrás probar esos cambios en la +plataforma. + +Cuando quieras empezar a ejecutar steps y probar tus cambios _localmente_, +consulta la documentación de la [CLI](/build-for-developers/cli-intro.md). diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/limits.md b/i18n/es/docusaurus-plugin-content-docs/current/build/limits.md new file mode 100644 index 000000000000..7dfe6324bf95 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/limits.md @@ -0,0 +1,87 @@ +--- +title: Límites +translation_source_hash: 322601a39382248bb796b091a3a0c3e3e7273be2 +translation_review_status: machine +--- + +La instancia de OpenFn alojada en la nube tiene varios límites que ayudan a que +todo funcione sin problemas. La siguiente tabla muestra los límites de los +distintos planes. Para ver una lista de límites más detallada, consulta la +[página de precios de OpenFn](https://openfn.org/pricing). En las instancias +autoalojadas, estos límites se pueden configurar. Consulta la +[guía de despliegue](https://openfn.github.io/lightning/deployment.html#limits) +para más detalles. + +| Funcionalidad | Descripción | DPG | Free | Core | Growth | Scale | Unlimited | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | ------------ | ----- | ----- | ------ | --------- | --------- | +| Runs | Número máximo de runs permitidos por mes | Ilimitado | 100 | 2000 | 5000 | 10 000 | Ilimitado | +| Duración de ejecución del workflow | Tiempo máximo que puede ejecutarse un workflow antes de que se detenga | Configurable | 60 s | 5 min | 20 min | 30 min | 30 min | +| Uso de memoria | Memoria máxima permitida por attempt del workflow | Configurable | 128MB | 256MB | 512MB | 1GB | 1GB | +| Tamaño del state | Tamaño máximo de los objetos state dentro de la VM del runtime (25 % del uso de memoria) | Dinámico | 32MB | 64MB | 128MB | 256MB | 256MB | +| Tamaño de los dataclips | Tamaño máximo de los dataclips guardados a partir de la salida de un run | Configurable | 512KB | 2MB | 10MB | 10MB | 10MB | +| AI Assistant | Máximo de tokens de IA disponibles | Configurable | 500K | 1.5M | 5M | 10M | 10M | +| Colecciones de datos (almacenamiento) | Almacenamiento máximo para colecciones de datos | Configurable | 1MB | 5MB | 10MB | 50MB | 50MB | +| Colecciones de datos (cantidad) | Número máximo de colecciones de datos por proyecto | Configurable | 2 | 5 | 10 | Ilimitado | Ilimitado | +| Control de concurrencia | Permite a los usuarios controlar los límites de concurrencia del proyecto | Configurable | Sí | Sí | Sí | Sí | Sí | + + + +:::tip Aumentar los límites en instancias alojadas en la nube y gestionadas + +En los planes estándar, puedes aumentar tus límites mejorando a un plan superior +con las +[instrucciones para mejorar tu plan](/hosted/overview.md#upgrading-your-subscription). + +Para límites personalizados o mejoras en despliegues dedicados, escribe a +enterprise@openfn.org. + +::: + +## Duración de ejecución del workflow (1 hora) {#workflow-execution-duration-1-hour} + +Cada attempt de un workflow debe completarse en menos de `1 hour`. Puedes ver la +duración de cada attempt haciendo clic en su ID. Si un attempt supera este +límite, el worker lo detiene y verás la insignia `Killed:Timeout` como estado +del attempt. + +> _Los superusuarios de la instancia pueden controlar este límite con la +> variable de entorno `MAX_RUN_DURATION`._ + +## Uso de memoria (1GB) {#memory-usage-1gb} + +Cada attempt de un workflow no puede usar más de `1GB` de memoria. Puedes ver el +uso máximo de memoria de cada attempt haciendo clic en su ID. Si un attempt +supera este límite, el worker lo detiene y verás la insignia `Killed:OOM` como +estado del attempt. + +> _Los superusuarios de la instancia pueden controlar este límite con la +> variable de entorno `MAX_RUN_MEMORY`._ + +Ten en cuenta que el objeto `state` que se devuelve al final de cada step de un +workflow no debe superar el 25 % del límite total de memoria del runtime, o tu +run se detendrá con un error `StateTooLarge`. + +## Tamaño de los dataclips (10MB) {#dataclip-size-10mb} + +1. Cada **solicitud de webhook** a una URL de trigger no puede superar `10MB`. +2. Si guardas el state final de cada **run** como dataclip, cada dataclip no + puede superar `10MB`. + + + + +Si envías a una URL de trigger webhook un payload que supera este límite, el +servidor responde con un error `413` y el mensaje `:request_entity_too_large`. + +Si los dataclips que genera el state final de los runs y los attempts son +demasiado grandes, no se guardan. El worker igual procesa los steps siguientes, +pero esos steps no se podrán reintentar, porque Lightning no guarda una copia de +los dataclips. Verás el error `ERROR: DataClip too large for storage` en los +logs del attempt. + +> _Los superusuarios de la instancia pueden controlar este límite con la +> variable de entorno `MAX_DATACLIP_SIZE`._ diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/paths.md b/i18n/es/docusaurus-plugin-content-docs/current/build/paths.md new file mode 100644 index 000000000000..cbcf13625783 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/paths.md @@ -0,0 +1,69 @@ +--- +title: Paths y condiciones de los paths +sidebar_label: Paths +translation_source_hash: f761fd26aaa7d8373dd681c3fd033f90dd0fef8a +translation_review_status: machine +--- + +Un path es una indicación, visual y funcional a la vez, que define la secuencia +de steps que sigue el workflow cuando se ejecuta. Sigue leyendo para conocer los +distintos tipos de paths y algunos consejos de configuración. + +## Condiciones de los paths {#path-conditions} + +Hay cuatro tipos de condiciones de path que definen si el workflow pasará al +siguiente step al ejecutarse: + +1. **Always**: el siguiente step siempre se ejecuta cuando termina la ejecución + del step anterior +2. **On Success**: el siguiente step solo se ejecuta si la ejecución del step + anterior _tuvo éxito_ +3. **On Failure**: el siguiente step solo se ejecuta si la ejecución del step + anterior _falló_ +4. **Matches a JavaScript Expression**: el siguiente step solo se ejecuta si una + expresión se evalúa como verdadera + +![Condiciones de los paths](/img/path_conditions.webp) + +## Escribir expresiones de JavaScript para condiciones de path personalizadas {#writing-javascript-expressions-for-custom-path-conditions} + +Crea una **condición personalizada** con una expresión de JavaScript. Se evalúa +con el state que produjo el step anterior. + +``` +state.data.form['@name'] === "Register New Patient" +``` + +Es una expresión normal de JavaScript con `state` en el ámbito. Si la expresión +se evalúa como verdadera (o como cualquier valor _truthy_), se sigue el path y +se ejecuta el siguiente step. + +![Condiciones personalizadas](/img/path_js_expression.webp) + +Algunos ejemplos de condiciones válidas: + +- Ejecutar si no hay errores: `!state.errors` +- Ejecutar si existe algún valor en el state: `state.has_valid_email_address` +- Ejecutar si un array de datos contiene algún elemento: `state.data.length > 0` +- Ejecutar si los datos incluyen un elemento que cumple un criterio: + `state.data.includes(item => item.age > 18)` +- Ejecutar si el último step recibió un error HTTP: + `state.response.statusCode >= 400` + +En una expresión personalizada **no puedes** hacer nada de lo siguiente: + +- Usar funciones de adaptors +- Usar referencias de lazy state (`$`). +- Usar sentencias de control como `if`, `while`, `for`, etc. + +## Deshabilitar paths {#disabling-paths} + +Deshabilitar un path impide que se ejecute cualquiera de los steps posteriores, +sin importar la condición ni el state. + +Es una forma útil de desactivar temporalmente una parte de tu workflow. + +Para deshabilitar un path: + +1. Haz clic en el `Path` que quieres desactivar +2. Marca la casilla `Disable this path` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/sandboxes.md b/i18n/es/docusaurus-plugin-content-docs/current/build/sandboxes.md new file mode 100644 index 000000000000..e33353d4a725 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/sandboxes.md @@ -0,0 +1,327 @@ +--- +title: Sandboxes +sidebar_label: Sandboxes +translation_source_hash: 45f6e0cb7820464901f65a1cb95e99a8f3cea5d0 +translation_review_status: machine +--- + +Los sandboxes te permiten desarrollar correcciones y funcionalidades nuevas en +tus workflows sin afectar los runs en vivo, o de "producción". + +Un sandbox es, en esencia, un clon de un proyecto, con su propio historial +privado, workflows, colecciones y configuración. Comparte las credenciales y la +facturación del proyecto principal, pero todo lo demás está aislado. + +La idea es que puedas desarrollar workflows totalmente aislados del proyecto +principal y, cuando termines, hacer merge de los cambios de vuelta (entiéndase +"enviarlos" o "promoverlos"). + +:::tip Sandboxes de corta duración + +Los sandboxes funcionan mejor cuando duran poco, por eso actualmente se +programan automáticamente para eliminarse después de hacer merge. Aunque puedes +crear todos los sandboxes que quieras (según el límite de uso de tu plan), +recomendamos tener pocos para reducir el riesgo de conflictos al hacer merge. + +::: + +## Contexto aislado {#isolated-context} + +Un sandbox es una copia aislada de tu proyecto original, con su propio contexto. +La mayoría de las cosas de un sandbox son privadas: + +- Workflows (jobs, edges y triggers; los triggers se deshabilitan al crearlo) +- Colecciones (se copian los nombres, no los datos) +- Credenciales de Keychain +- Miembros del proyecto (se copian del proyecto principal al crearlo y después + son independientes) +- La mayor parte de la configuración del proyecto (también se copia del proyecto + principal y después es independiente) + +El historial de runs y los dataclips no se copian: los sandboxes empiezan con un +historial vacío y acumulan el suyo a medida que se ejecutan los workflows. + +Algunas cosas se comparten con el proyecto principal en lugar de copiarse: + +- **Credenciales.** Las mismas credenciales siguen disponibles en el sandbox; + solo se duplica el vínculo entre la credencial y el proyecto. Editar una + credencial afecta a todos los proyectos que la usan. +- **Suscripción.** Los runs y los tokens de IA que se usan en un sandbox cuentan + para los límites de uso del proyecto principal. +- **Métodos de autenticación de webhook.** Están en el proyecto principal y se + comparten: los triggers webhook del sandbox hacen referencia a los métodos del + proyecto principal. La pestaña Webhook Security del sandbox es de solo lectura + y enlaza al proyecto principal para gestionarlos. Cada trigger webhook del + sandbox sigue teniendo su propia URL única. + +## Crear sandboxes {#creating-sandboxes} + +Para crear un sandbox, debes ser `editor`, `admin` u `owner` del proyecto +principal. Entra al proyecto principal, haz clic en **Sandboxes** en el menú +lateral y luego en **Create Sandbox**. + +![Lista de sandboxes](/img/sandboxes_list.webp) + +Tienes que ponerle un nombre al sandbox. Debe ser único entre los sandboxes del +proyecto. Si conoces git, trátalo como el nombre de una rama. Si no, puedes +darle un nombre general, como `testing`, o nombrarlo según una funcionalidad +concreta, como `new-patient-workflow`. + +Se elige un color al azar para asociarlo al sandbox. Verás ese color en el +selector de proyectos de la ruta de navegación mientras estés dentro del +sandbox, para que sepas fácilmente dónde estás. Si quieres, puedes elegir otro +color. + +![Ventana modal Create Sandbox](/img/create_sandbox_modal.webp) + +Cuando estés listo, haz clic en **Create Sandbox**. Entrarás automáticamente al +sandbox. + +Al crear el sandbox, se copian los miembros del proyecto principal: quien lo +crea pasa a ser `owner` del sandbox, los propietarios del proyecto principal +pasan a `admin` y los demás miembros mantienen su rol original. A partir de ahí, +el sandbox gestiona sus propios miembros: agregar o quitar a alguien en el +proyecto principal no afecta a los sandboxes que ya existen debajo. + +Todos los triggers de workflow del nuevo sandbox empiezan **deshabilitados**. +Así se evita duplicar runs de producción desde el momento en que existe el +sandbox. Puedes volver a habilitar cualquier trigger que quieras probar desde la +página del workflow en el sandbox. + +Después de crearlo, la acción Edit de la tarjeta de un sandbox te permite +renombrarlo y cambiar su color. Todo lo demás (workflows, credenciales, +miembros, entorno y el resto de la configuración) se gestiona desde dentro del +propio sandbox. + +### Límites {#limits} + +Hay dos límites prácticos para crear sandboxes: + +- **Cantidad de sandboxes activos.** Tu suscripción incluye un tope de sandboxes + activos (no programados para eliminarse) que puedes tener. Cuando llegas al + tope, el botón **Create Sandbox** se deshabilita y muestra un mensaje + emergente que explica por qué. Los sandboxes programados para eliminarse no + cuentan, así que puedes liberar un lugar eliminando uno que ya no necesites. +- **Profundidad de anidamiento.** Un sandbox puede tener a su vez sandboxes + debajo, y así sucesivamente, hasta una profundidad configurable (5 por + defecto). Cuando el proyecto principal llega al tope, el botón **Create + Sandbox** de su página de sandboxes se deshabilita y muestra el mensaje + emergente "Maximum sandbox nesting depth reached". + +## Quién puede hacer qué {#who-can-do-what} + +El acceso a los sandboxes funciona igual que en los proyectos normales: lo que +puedes ver y hacer depende de tu rol en el proyecto sobre el que actúas. El enum +`User.role` (`:user` / `:superuser`) es un tipo de usuario para las pantallas +globales de gestión de usuarios, no un rol de proyecto. + +| Acción | Rol necesario | +| ----------------------------------- | ---------------------------------------------------------------------------------------------- | +| Ver un sandbox | Fila directa en `project_users`, o usuario de soporte con `allow_support_access` en el sandbox | +| Abrir un sandbox | Igual que el anterior | +| Crear un sandbox | `editor`, `admin` u `owner` en el proyecto principal | +| Editar o eliminar un sandbox | `admin` u `owner` en el propio sandbox, o `admin`/`owner` en la raíz del espacio de trabajo | +| Hacer merge de un sandbox | `admin` u `owner` en el origen, y `editor` o superior en el destino del merge | +| Cancelar una eliminación programada | Igual que para editar o eliminar | + +El selector global de proyectos oculta los proyectos que no puedes ver. + +## Ver un sandbox {#viewing-a-sandbox} + +Puedes ver todos los sandboxes de un proyecto en la página **Sandboxes** del +menú lateral. Desde ahí, haz clic en el nombre de un sandbox para entrar. +También puedes entrar a un sandbox desde el selector global de proyectos +(Ctrl/Cmd+P). + +Cuando estás dentro de un sandbox, el selector de proyectos de la ruta de +navegación muestra el nombre del sandbox y toma el color elegido para él, así +sabes fácilmente qué versión de tu proyecto estás viendo. + +![Ruta de navegación dentro de un sandbox](/img/sandbox_breadcrumb.webp) + +Cada sandbox tiene sus propios workflows, colecciones, historial y +configuración, aislados. Al recorrer las páginas, notarás que no aparecen los +datos de tu proyecto original. Esto se debe a que tu sandbox es un clon +independiente del proyecto original. + +Cada pestaña de la página Settings muestra un aviso que explica cómo se +relacionan sus cambios con el proyecto principal: + +- **Sandbox Identity, Collaboration, Sync to GitHub, Data Storage, History + Exports**: los cambios son privados del sandbox y no se sincronizan al hacer + merge. +- **Credentials**: los cambios se sincronizan con el proyecto principal al hacer + merge. +- **Security**: de solo lectura en el sandbox; se gestiona en el proyecto + principal. +- **Webhook Security**: lo gestiona el proyecto principal. Los métodos se + comparten y se aplican a los triggers webhook del sandbox, pero solo se pueden + crear, editar o eliminar desde el proyecto principal. + +![Aviso de configuración del sandbox en la pestaña Credentials](/img/sandbox_settings_banner.webp) + +## Entornos {#environments} + +Los entornos te permiten ejecutar un workflow con un conjunto especial de +valores de credenciales, separado del de tu proyecto principal. Así puedes usar +servidores, modos y bases de datos de desarrollo mientras trabajas en tu +sandbox, sin interferir con los servicios de producción en vivo. + +El entorno es solo una etiqueta, y cada credencial que usa tu workflow tiene un +conjunto de valores asociados a esa etiqueta. Por ejemplo, al conectarte a +DHIS2, tu credencial principal tendrá datos de inicio de sesión privados, pero +tu entorno `dev` podría usar el sandbox público y tener un nombre de usuario y +una contraseña diferentes. + +Por defecto, todos los sandboxes reciben el entorno `dev`. Puedes cambiarlo +desde la página Settings. + +Todos los entornos se guardan cifrados y de forma segura en nuestra base de +datos, así que es totalmente seguro duplicar credenciales de producción en +varios entornos. + +Para cada credencial que use tu workflow, debes asegurarte de que haya un valor +configurado para el entorno de tu sandbox. Si no configuras tus credenciales, el +workflow fallará con instrucciones claras para corregirlo. + +## Hacer merge de sandboxes {#merging-sandboxes} + +Cuando termines de hacer cambios en tus workflows, es hora de hacer merge de +esos cambios de vuelta en el proyecto principal (o en cualquier otro proyecto en +el que puedas escribir). + +Ve a la página **Sandboxes**, busca el sandbox en la lista y haz clic en el +ícono **Merge** de la derecha. Se te pedirá que elijas el proyecto de destino +del merge. Normalmente querrás hacer merge en el proyecto principal original, +que viene seleccionado por defecto. + +También puedes elegir qué workflows incluir en el merge. Esto ayuda a reducir +los conflictos con cambios en el proyecto de base y a entender las consecuencias +del merge. + +Al hacer merge, reemplazamos el contenido de los workflows del proyecto de +destino con el de tu sandbox. Si renombras un workflow, parecerá que se eliminó +de la base y se agregó un workflow nuevo. + +El merge de las colecciones es por relación, no por datos. Una colección que +solo existe en el sandbox se crea vacía en el destino. Una colección que solo +existe en el destino se elimina (junto con sus elementos). Una colección con el +mismo nombre en ambos lados no se toca: no se copian los elementos de ninguno de +los dos. + +Para hacer merge de un sandbox, debes ser `admin` u `owner` del **origen** (el +sandbox desde el que haces merge); si no, el botón Merge está deshabilitado. +También necesitas el rol `editor` o superior en el proyecto de destino; si no, +se rechaza el merge. + +Después del merge, el sandbox de origen queda **programado para eliminarse** +tras el período de gracia configurado. Pasa a la sección "Scheduled for +deletion" de la lista de sandboxes, donde puedes restaurarlo durante ese plazo +si cambias de opinión. Los entornos y las credenciales asociados al proyecto no +se ven afectados. + +Si el sandbox del que haces merge tiene sus propios sandboxes debajo, también se +programan para eliminarse. La ventana modal de confirmación del merge te muestra +cuántos descendientes se retirarán junto con el origen. + +![Ventana modal para hacer merge de un sandbox](/img/merge_sandbox_modal.webp) + +## Conflictos {#conflicts} + +Si alguna vez trabajaste con un sistema de control de versiones de código, como +git o Subversion, ya conocerás la idea de los conflictos. + +Puede haber un conflicto cuando intentas hacer merge de un sandbox en un +proyecto de destino y el destino cambió desde que se creó el sandbox. Por +ejemplo, cambias el adaptor de un step en el sandbox de `common` a `http`, +mientras que un colega cambia el adaptor de ese mismo step en el proyecto +principal a `salesforce`. No hay forma automática de combinar esos cambios. + +Para ayudarte a ver qué pasa, la ventana modal de confirmación del merge marca +cada workflow con una de cuatro etiquetas: + +- **Changed**: el workflow se modificó en el sandbox y se reemplazará la copia + del destino. +- **Diverged**: el workflow se modificó en el destino después de crear el + sandbox. Si lo incluyes en el merge, se sobrescribirán esos cambios del + destino. Los workflows divergentes se marcan con un ícono de advertencia + ámbar. +- **New**: el workflow no existe en el destino y se creará. +- **Deleted in sandbox**: el workflow se eliminó en el sandbox. Si lo incluyes, + se elimina del destino. + +Tú eliges qué workflows incluir. Seleccionar uno **Diverged** es decidir de +forma explícita sobrescribir la versión del destino con la del sandbox. No hay +una herramienta para resolver conflictos en la aplicación: o aceptas la versión +del sandbox o dejas el workflow fuera. + +Puedes usar git y la CLI para resolver localmente los conflictos de los +workflows divergentes: consulta +[Resolver conflictos de merge](/build-for-developers/cli-sync.md#resolving-merge-conflicts). + +Si necesitas combinar cambios de ambos lados, descarga los dos proyectos con la +CLI, resuelve las diferencias localmente y vuelve a enviar el resultado. + +## Restaurar un sandbox eliminado {#restoring-a-deleted-sandbox} + +Cuando se elimina un sandbox, ya sea de forma explícita o como parte de un +merge, no se borra de inmediato. El sandbox (y cualquier sandbox que tenga +debajo) queda programado para eliminarse, con todos los triggers de sus +workflows deshabilitados, y un worker lo borra definitivamente cuando termina el +período de gracia configurado. + +Durante ese plazo, el sandbox aparece en la sección "Scheduled for deletion", al +final de la lista de sandboxes. Cualquier persona con permisos de `admin` u +`owner` en el sandbox (o en la raíz del espacio de trabajo) puede hacer clic en +**Restore** para cancelar la eliminación programada. Al restaurarlo, se +reactivan el sandbox y sus descendientes; los triggers siguen deshabilitados y +hay que volver a habilitarlos a mano. + +![Sección Scheduled for deletion](/img/scheduled_for_deletion.webp) + +Cuando termina el período de gracia y se ejecuta el worker de purga, el sandbox +desaparece para siempre. + +## Editar sandboxes localmente {#editing-sandboxes-locally} + +Los sandboxes son totalmente compatibles con la CLI. + +Usa `openfn project pull` para descargar un sandbox localmente y +`openfn project push` para enviar los cambios de vuelta al sandbox en la +aplicación. + +Puedes usar `openfn project merge` para hacer merge de dos proyectos locales y +luego `openfn project deploy` para sincronizarlos con la aplicación. + +Si trabajas con varios sandboxes en un mismo espacio de trabajo, hay dos cosas +que ayudan: + +- `openfn project fetch` asigna alias a los sandboxes automáticamente. Cuando + descargas un sandbox sin indicar `--alias`, la CLI usa el id del sandbox como + alias, para que varios sandboxes puedan convivir en el mismo espacio de + trabajo sin chocar con el proyecto principal ni entre sí. +- `openfn project checkout ` cambia el proyecto activo del espacio de + trabajo. Úsalo para pasar de un sandbox a otro entre los que ya descargaste. + +## Buenas prácticas {#best-practices} + +Los sandboxes se corresponden muy bien con las ramas de git, y las formas de +trabajo que mejor funcionan los tratan de la misma manera. + +**Crea un sandbox por cada tarea.** Dale a cada funcionalidad nueva, corrección +o issue su propio sandbox, en lugar de compartir un único sandbox `testing` de +larga duración entre varias personas y varios cambios. Los sandboxes pequeños y +de corta duración son más fáciles de revisar y es mucho menos probable que +generen conflictos al hacer merge. + +**Apila sandboxes cuando un trabajo depende de otro anterior.** Si un sandbox +tiene que existir durante un tiempo (por ejemplo, mientras espera una revisión o +una ventana de lanzamiento) y quieres empezar algo que se apoya en él, crea un +sandbox de ese sandbox en lugar de hacer merge antes de tiempo solo para +desbloquearte. Los sandboxes se pueden anidar hasta la profundidad configurada. +Haz lo mismo en git: crea la rama del trabajo dependiente a partir de la rama de +la funcionalidad, no de main. + +**Evita trabajar directamente en el proyecto principal.** Los cambios que hagas +ahí están en vivo y aparecen como workflow divergente en el merge de todos los +demás sandboxes. Empieza en un sandbox y haz merge cuando el cambio esté listo. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/steps/step-design-intro.md b/i18n/es/docusaurus-plugin-content-docs/current/build/steps/step-design-intro.md new file mode 100644 index 000000000000..76f34dc9aba4 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/steps/step-design-intro.md @@ -0,0 +1,90 @@ +--- +title: Diseña tu step +translation_source_hash: 00a872e0912678e679517b3b01dfd0e645c71d84 +translation_review_status: machine +--- + +Antes de configurar un step de un workflow, tienes que diseñar el workflow +completo y pensar qué tareas, actividades o lógica de negocio concretas +ejecutará cada step. Sigue leyendo para ver un breve resumen. + +:::tip + +Consulta la documentación de [diseño de workflows](/design/design-overview.md) +para ver más detalles sobre el diseño de soluciones y enlaces a plantillas. + +::: + +En resumen, para diseñar un step de un workflow, tienes que seguir la lista de +acciones de abajo y considerar resumir las especificaciones de tu diseño en un +[diagrama del workflow](/design/design-workflow.md). + +![Ejemplo de workflow](/img/example-workflow-state.webp) + +## 1. Determina tus entradas y salidas {#1-determine-your-inputsoutputs} + +1. ¿Cuál es la entrada de este step del workflow? Piensa en cuál es el state + inicial, o los datos, _antes_ de que empiece el step. +2. ¿Cuál es la salida que quieres (es decir, el state final, o los datos, + _después_ de que se ejecute el step)? Piensa en qué quieres enviar a la + aplicación de destino o pasar al siguiente step del workflow. + +## 2. Mapea tus elementos de datos {#2-map-your-data-elements} + +[Consulta esta página](/design/mapping-specs.md) para ver una guía detallada +sobre cómo mapear los elementos de datos, o "diccionarios de datos", entre tus +aplicaciones de origen y de destino. Para empezar: + +1. Exporta los metadatos (o el "formulario", la "lista de campos" o los + "elementos de datos") de tu aplicación de origen (entrada) y de tu aplicación + de destino (salida). +2. Pega los metadatos en una hoja de cálculo de Excel para crear una hoja de + mapeo: + +![Ejemplo de hoja de mapeo](/img/data-element-mapping.webp) + +3. Mapea los elementos de datos de origen y de destino, y define reglas de + limpieza y transformación de los datos. Piensa en lo siguiente: + +- ¿Cómo se deberían traducir los datos recolectados al modelo de datos de tu + sistema de destino? +- ¿Tu sistema de destino tiene requisitos de entrada o de validación de datos? +- ¿Hay que transformar o limpiar los datos para cumplir con las respuestas + anteriores? + +## 3. Define tus métodos (GET, POST...) u operaciones (insert, update, upsert...) {#3-define-your-methods-get-post-andor-operations-insert-update-upsert} + +1. Determina qué identificadores únicos existentes puedes usar para tus datos, o + créalos. Los identificadores únicos sirven para insertar y actualizar + registros concretos de tus datos (por ejemplo, uuid, form_id, patient_id, + etc.). +2. Determina qué métodos HTTP (por ejemplo, GET, POST, PUT) u operaciones de + base de datos (por ejemplo, insert, update, delete) quieres hacer en la + aplicación de destino +3. Revisa las funciones auxiliares del adaptor. + - Ejemplo de [language-postgresql](/adaptors/packages/postgresql-docs) + - `insert(...)`, `insertMany(...)` + - `update(...)`, `updateMany(...)` + - `upsert(...)`, `upsertMany(...)` → actualiza el registro si existe o lo + inserta si no existe; hace referencia a un ID externo + - Ejemplo de [language-dhis2](/adaptors/packages/dhis2-docs) con Tracked + Entity Instances (TEI) + - `updateTEI(...)` + - `upsertTEI(...)` + +Mira el siguiente ejemplo de `Job expression` para un step que hace un "upsert" +(actualiza o inserta) de registros en una base de datos SQL. + +```js +upsert('mainDataTable', 'AnswerId', { + AnswerId: dataValue('\_id'), //external Id for upsert + column: dataValue('firstQuestion)'), + LastUpdate: new Date().toISOString(), + Participant: dataValue('participant'), + Surveyor: dataValue('surveyor'), + ... +}); +``` + +Consulta la [guía para escribir jobs](/jobs/job-writing-guide.md) para más +información. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/steps/step-editor.md b/i18n/es/docusaurus-plugin-content-docs/current/build/steps/step-editor.md new file mode 100644 index 000000000000..bce0b793481c --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/steps/step-editor.md @@ -0,0 +1,61 @@ +--- +title: Editar steps desde el Inspector +sidebar_label: Editar y probar steps +translation_source_hash: b452d22eaf108275e720a809f667abd2111ac105 +translation_review_status: machine +--- + +Esta página explica cómo editar y probar los steps de tu workflow con la +interfaz del Inspector. + +:::tip + +Si escribes jobs en la aplicación de la plataforma (Lightning), puedes usar el +[AI Assistant](/build/ai-assistant.md) para que te ayude. Lo encontrarás en el +Inspector. + +::: + +## Editar y probar steps desde el Inspector {#edit--test-steps-via-the-inspector} + +Usa la interfaz del `Inspector` de la plataforma para crear, editar y probar +steps. (Si creaste tu workflow localmente con la CLI, puedes editar tus jobs en +la aplicación desde esta interfaz). + +Para acceder a esta interfaz: + +1. Abre un workflow +2. Selecciona el step que quieres editar o probar +3. Haz clic en el botón de código `` del panel de configuración + +Para saber más sobre cómo escribir lógica de negocio personalizada y reglas de +transformación de datos en el `Editor`, consulta la documentación sobre +[cómo escribir jobs](/jobs/job-writing-guide.md) y mira el video de abajo. + + + +## Ejecutar y probar steps {#run--test-steps} + +Cuando ejecutas steps para probar la configuración, cada run tiene un state +inicial (que puede contener un `Input`) y da como resultado un state final que +incluye `Logs` y un `Output`. + +- `Input`: datos (JSON) que un step usa como entrada inicial en su run. Puede + haber una entrada para una work order y para cada step de un run, aunque + cualquiera de los dos puede existir sin entrada. +- `Output`: datos (JSON) que se crean como salida de la ejecución de un step. + Puede haber una salida para una work order y para cada job de un run, y suele + contener los datos enviados a la aplicación de destino. +- `Logs`: un registro que genera el motor de ejecución de workflows con el + detalle de las actividades realizadas al ejecutar un workflow o un step. + +Consulta la [documentación sobre cómo escribir jobs](/jobs/job-writing-guide.md) +para saber más sobre cómo escribir lógica personalizada, y +[este artículo](/jobs/state.md) para saber más sobre el concepto de "state" al +escribir jobs y crear workflows de OpenFn. + +## Atajos de teclado {#keyboard-shortcuts} + +Desde el Inspector puedes hacer algunas acciones comunes (por ejemplo, guardar, +ejecutar o sincronizar con GitHub) con el teclado. Consulta la lista completa de +atajos de teclado [aquí](/keyboard-shortcuts.md). diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/steps/steps.md b/i18n/es/docusaurus-plugin-content-docs/current/build/steps/steps.md new file mode 100644 index 000000000000..ee5a5bbd18d4 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/steps/steps.md @@ -0,0 +1,156 @@ +--- +title: Configurar steps +translation_source_hash: 157984301b15b94a4df45d57fcf4b28494fa4e69 +translation_review_status: machine +--- + +Un step es una tarea o actividad concreta dentro de un workflow. Cada step está +vinculado a un [adaptor](/adaptors/) y contiene la lógica de negocio para hacer +una tarea u operación concreta en esa aplicación de destino. + +:::note + +En OpenFn V1 no existía el concepto de `Workflow Steps`: se llamaban `Jobs`. En +V2, los `Jobs` se definen como _las expresiones de job o los scripts que definen +la lógica de negocio y las reglas de transformación de cada uno de los `Steps`_. + +::: + +## Crear o editar un step {#create-or-edit-a-step} + +En el Canvas del workflow, haz clic en el ícono de más `+` para crear un step +_nuevo_, o haz clic en un step existente para ver o configurar sus componentes +principales. + +## Configurar el step {#configure-the-step} + +Para configurar bien un step, tienes que entender su anatomía básica. + +![Anatomía de un step](/img/anatomy_of_step.webp) + +Un step incluye estos componentes principales: + +- `Name`: un nombre legible que describe el step y su propósito. +- `Adaptor`: el [adaptor](/adaptors/) seleccionado, que aporta la funcionalidad + específica de la aplicación para este step (por ejemplo, `dhis2` o + `commcare`). +- `Adaptor Version`: la versión del adaptor seleccionado, que determina qué + endpoints de la API y qué funciones del adaptor están disponibles. Consulta la + sección [Elige una versión del adaptor](#3-choose-an-adaptor-version) más + abajo para saber más. +- `Credentials`: la credencial que se usa para autorizar las conexiones con la + aplicación de destino de este step. +- `Job`: el código personalizado que define la lógica de negocio o la secuencia + de operaciones que se ejecutan en la aplicación conectada, o ambas. + +:::tip Escribir jobs + +Escribir jobs para agregar lógica personalizada con reglas de negocio o de +transformación de datos suele requerir conocimientos básicos de JavaScript. +Consulta la [documentación sobre cómo escribir jobs](/jobs/job-writing-guide.md) +para ver una descripción detallada, y los +[ejemplos de la biblioteca](/adaptors/library) para ver código de muestra. + +::: + +## 1. Ponle nombre a tu step {#1-name-your-step} + +Primero, dale a tu step un `Name` que describa su propósito (por ejemplo, +`create patient`, `map form data`). + +## 2. Elige un adaptor {#2-choose-an-adaptor} + +Después, selecciona un `Adaptor` para definir con qué aplicación se conectará tu +step. + +:::tip + +Cada step solo puede tener 1 adaptor. Si quieres conectarte con 2 aplicaciones +distintas, deberías crear 2 steps distintos. + +::: + +Tenemos una sección completa sobre cómo crear [adaptors](/adaptors) nuevos, pero +lo más importante que debes saber al escribir un step es que tienes que elegir +un **adaptor** y una **versión del adaptor**. + +Todo lo que se explica más abajo sobre funciones auxiliares como `create` o +`findPatient` requiere entender un poco los adaptors. Cuando ejecutas un step, +usas una capa de funcionalidad que se construyó para conectarse con una API, un +tipo de API o una base de datos concretos. + +Por ejemplo, `create` significa una cosa en el adaptor `salesforce` y otra +completamente distinta en `dhis2`. Por eso, antes de empezar a escribir un step, +tienes que decidir con qué [adaptor](/adaptors/) vas a trabajar. + +### 3. Elige una versión del adaptor {#3-choose-an-adaptor-version} + +Elige la versión del adaptor que quieres usar. Te recomendamos seleccionar la +última versión disponible, salvo que quieras usar una versión anterior que sea +compatible con una versión anterior de la API con la que te conectas. Consulta +la [documentación de los adaptors](/adaptors) para ver los detalles de cada uno. + +Los adaptors cambian con el tiempo. Son de código abierto y fomentamos todas las +contribuciones posibles: publicamos versiones nuevas para usarlas en OpenFn.org +en cuanto pasan nuestras revisiones de seguridad. Puede que se agreguen +funcionalidades nuevas y se corrijan errores, pero, para asegurarte de que una +integración existente no se rompa, te recomendamos seleccionar una versión +concreta (en lugar de usar la funcionalidad de "actualización automática") +cuando elijas un adaptor. La versión publicada más alta es la opción +predeterminada. + +:::tip + +Las _primeras 4 líneas_ del log de cualquier run en OpenFn te dicen qué adaptor +estás ejecutando (además de la versión del worker, del engine y de Node.js). +Esto es muy importante, sobre todo si intentas solucionar problemas de steps en +distintos entornos (como tu propia terminal, app.openfn.org, etc.). + +::: + +Fíjate bien en qué `version` usas para escribir un step. Observa los siguientes +logs de un run: + +```sh +Versions for run f470a3da-8b90-480e-a94f-6dd982c91afe: + ▸ node.js 18.19.0 + ▸ worker 0.5.0 + ▸ engine 0.2.6 + ▸ @openfn/language-primero 2.9.1 +...more logs here... +``` + +#### Gestionar las versiones de los adaptors {#managing-adaptor-versions} + +Aunque actualizar puede ser útil como parte del mantenimiento habitual, estas +actualizaciones se deberían probar con cuidado. Lo más común es que los clientes +actualicen a una versión nueva del adaptor de un step existente cuando ya están +haciendo cambios en ese step por motivos de negocio. Algunos cambios de negocio +pueden incluso _requerir_ actualizar la versión para usar una funcionalidad +nueva del adaptor. Aunque esos cambios no requieran una actualización, si el +equipo técnico tiene que dedicar tiempo de todos modos a probar cambios en un +step, puede ser el momento ideal para probar también una actualización de la +versión del adaptor. + +Los adaptors siguen [SEMVER](https://semver.org/), así que puedes estar +razonablemente seguro de que actualizar de `x.1.z` a `x.2.z` no hará que falle +el código existente de un step, pero actualizar de `3.y.z` a `4.y.z` sí podría: +en SEMVER, las actualizaciones _mayores_ (las que cambian el primer número de la +versión `x.y.z`) tienen cambios "incompatibles" o "no retrocompatibles". + +:::tip + +Al configurar un step, puedes seleccionar una `Adaptor Version` concreta para +fijar la versión de tu step. Si eso es lo que quieres, y para evitar el riesgo +de actualizaciones accidentales en workflows en producción, no selecciones +`latest` como versión del adaptor. + +::: + +### 4. Escribe un job para la lógica de negocio personalizada o las reglas de transformación de datos {#4-write-a-job-for-custom-business-logic-or-data-transformation-rules} + +Haz clic en el botón de código `` del panel de configuración para escribir o +editar una expresión de job que defina las "reglas" o las tareas concretas que +debe completar tu step. Consulta las páginas sobre +[el Inspector](/build/steps/step-editor.md) y sobre +[cómo escribir jobs](/jobs/job-writing-guide.md) para saber más. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/triggers.md b/i18n/es/docusaurus-plugin-content-docs/current/build/triggers.md new file mode 100644 index 000000000000..c011fb328992 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/triggers.md @@ -0,0 +1,276 @@ +--- +title: Triggers +translation_source_hash: 151798ebc62b4d1787b5ce6d9d182260779897e3 +translation_review_status: machine +--- + +Los triggers permiten iniciar la ejecución de workflows automáticamente. Hay dos +tipos: triggers cron y triggers de eventos webhook. + +## Triggers de eventos webhook {#webhook-event-triggers} + +Los **triggers de eventos webhook** escuchan solicitudes HTTP entrantes +(mensajes de otros sistemas) y permiten una automatización en tiempo real, +basada en eventos. + +Estos triggers se disparan cuando se "empujan" datos a OpenFn (es decir, cuando +se envía una solicitud HTTP "POST" a la URL asignada a tu trigger). + +La solicitud HTTP que dispara el trigger puede llegar desde un webhook de una +aplicación externa, desde otro workflow de OpenFn o enviarse manualmente (es +decir, con una solicitud de cURL). + +![Trigger webhook](/img/webhook_trigger.webp) + +Para aprender a agregar una capa extra de seguridad a tu trigger webhook con +autenticación, ve a la página +[Seguridad de los webhooks](/manage-projects/webhook-auth.md). + +Aprende cómo se construye el `state` inicial de un workflow a partir de un +trigger webhook [aquí](/jobs/state.md#webhook-triggered-runs). + +## **Respuestas del trigger webhook** {#webhook-trigger-responses} + +Cuando un webhook dispara un workflow, OpenFn puede responder al sistema que +hizo la llamada de una de dos formas, según cómo esté configurado el trigger. + +### **El modo asíncrono responde antes de empezar** {#async-mode-responds-before-start} + +De forma predeterminada, los workflows se ejecutan de forma **asíncrona**. + +OpenFn envía una respuesta HTTP **en cuanto recibe la solicitud del webhook**, +una vez creados la work order y el run. El sistema que hizo la llamada recibe +una confirmación rápida y el workflow se ejecuta en segundo plano. + +**Usa este modo cuando:** + +- El sistema que hace la llamada solo necesita confirmar que se recibió la + solicitud +- Quieres respuestas rápidas y poca dependencia entre los sistemas +- El sistema que hace la llamada no necesita el resultado del run del workflow + +**Respuesta:** + +- Código de estado: `200` +- Encabezados: + - `x-meta-work-order-id`: el ID de la work order creada + - `x-meta-run-id`: el ID del run creado +- Cuerpo: + ```json + { + "work_order_id": "abc123", + "run_id": "xyz456" + } + ``` + +### **El modo síncrono responde al terminar** {#sync-mode-responds-after-completion} + +Si quieres, los workflows también se pueden ejecutar de forma **síncrona**. + +OpenFn mantiene abierta la conexión HTTP y envía una respuesta **cuando termina +el run**, con el state final del run como cuerpo de la respuesta. El sistema que +hizo la llamada espera el resultado, a veces durante segundos o minutos. + +**Usa este modo cuando:** + +- El sistema que hace la llamada necesita el resultado del run del workflow +- Necesitas la salida final del workflow para decidir el siguiente paso en el + sistema que hace la llamada + +**Respuesta predeterminada:** + +- Código de estado: `201` (configurable; ver más abajo) +- Encabezados: + - `x-meta-work-order-id`: el ID de la work order + - `x-meta-run-id`: el ID del run +- Cuerpo: un objeto JSON con esta forma: + ```json + { + "data": { /* the final run state */ }, + "meta": { + "work_order_id": "abc123", + "run_id": "xyz456", + "state": "success", + "error_type": null, + "inserted_at": "2026-05-21T10:00:00Z", + "started_at": "2026-05-21T10:00:01Z", + "claimed_at": "2026-05-21T10:00:01Z", + "finished_at": "2026-05-21T10:00:05Z" + } + } + ``` + - `data`: el state final del run (o un mensaje de seguridad si falla, o un + cuerpo personalizado; ver más abajo) + - `meta`: los metadatos del run, con las marcas de tiempo de su ciclo de vida + y el `state` final (`"success"` o `"failed"`) + +:::note Política de seguridad para los runs fallidos + +Cuando un run falla, OpenFn devuelve un mensaje genérico en `data` en lugar del +state completo del run, para no filtrar datos sensibles. Aun así, puedes +devolver un cuerpo personalizado desde un run fallido con `webhookResponse` (ver +más abajo). + +::: + +#### Configurar códigos de estado personalizados {#configuring-custom-status-codes} + +En el modo síncrono puedes configurar códigos de estado HTTP personalizados en +el trigger (en **Options → Response Status** en la interfaz): + +- **Success Status Code**: se devuelve cuando el run termina con éxito (el valor + predeterminado es `201`) +- **Error Status Code**: se devuelve cuando el run falla (el valor + predeterminado es `201`) + +:::note Volver al modo asíncrono + +Si vuelves a cambiar un trigger al modo asíncrono, se borran los códigos de +estado de éxito o de error que hayas configurado, porque solo se aplican en el +modo síncrono. + +::: + +#### Personalizar la respuesta desde tu job {#customising-the-response-from-your-job} + +Para devolver un cuerpo o un código de estado personalizados a partir de valores +en tiempo de ejecución, define `webhookResponse` en el state, por ejemplo: + +```js +fn(state => ({ + ...state, + webhookResponse: { + status: 200, + body: { ack: true, id: state.data.id }, + }, +})); +``` + +Al final del run, se usa el valor de `state.webhookResponse` para enviar la +respuesta HTTP al sistema que hizo la llamada. Cambiar el valor durante el run +no afecta a la respuesta: solo cuenta el state final. + +Tanto `status` como `body` son **opcionales**: puedes incluir uno de los dos o +ambos: + +| Campo | Comportamiento cuando se define | +| -------- | -------------------------------------------------------------------- | +| `status` | Reemplaza el código de estado configurado para este run | +| `body` | Reemplaza el state final del run en `data` en el cuerpo de respuesta | +| ninguno | Usa el código de estado configurado y el state final del run | + +`webhookResponse.body` solo reemplaza la parte `data` de la respuesta: OpenFn +siempre incluye `meta`. Así que el ejemplo de arriba produce: + +```json +{ + "data": { "ack": true, "id": "..." }, + "meta": { "work_order_id": "...", "run_id": "...", "state": "success", ... } +} +``` + +:::note Valores mal formados + +Si `webhookResponse` no es un objeto JSON (por ejemplo, si es una cadena, un +número o un array), se ignora y se aplican el código de estado y el cuerpo +predeterminados del run. + +Si `webhookResponse.status` no es un número entero, o `webhookResponse.body` no +es un objeto JSON, el código de estado de la respuesta vuelve al predeterminado +del run (el código de éxito o de error configurado, o `201`) y `data` se +reemplaza por +`{ "message": "Run completed, but webhook_response was malformed: ..." }`. + +::: + +## Triggers cron {#cron-triggers} + +Los **triggers cron** ejecutan workflows según una programación cron, y sirven +para tareas repetitivas basadas en el tiempo (por ejemplo, sincronizar datos +financieros entre dos sistemas todos los días a las 8 a. m.). + +Estos triggers permiten "extraer" datos de los sistemas conectados. Puedes +elegir una programación estándar (por ejemplo, todos los días o todos los meses) +o definir una programación personalizada con expresiones cron. + +:::tip Ayuda con las expresiones cron + +Si todavía no conoces `cron`, la mejor forma de aprender es desde la interfaz de +OpenFn o en crontab.guru. + +::: + +Con los triggers cron, los workflows se pueden ejecutar con una frecuencia de +hasta una vez por minuto, o con la poca frecuencia que quieras, y se pueden +programar en fechas u horas muy concretas. + +### `state` de entrada para el siguiente run {#input-state-for-the-next-run} + +Cada vez que se ejecuta un workflow con trigger cron, _empieza_ con la salida +final del último run exitoso. Así puedes crear workflows que usen un +["cursor"](/jobs/using-cursors.md) para saber qué pasó la última vez que se +ejecutó el workflow. (Por ejemplo, para procesar solo los datos que cambiaron +desde ese último run). + +![Trigger cron](/img/cron_trigger.webp) + +De forma predeterminada, el state de entrada del siguiente run cron será el +state de salida final del run anterior, pero puedes configurarlo para que use el +state de salida de un step concreto de ese run anterior cambiando el "Cron Input +Source". + +Encontrarás más información sobre el `state` en los runs con trigger cron en la +documentación de +["State de entrada y de salida"](/jobs/state.md#cron-triggered-runs). + +### Controlar el tamaño del `state` en los workflows cron {#managing-the-size-of-state-for-cron-workflows} + +Como el state pasa de un run al siguiente en un workflow cron, si un step de tu +workflow agrega algo nuevo al state cada vez que se ejecuta, el state puede +crecer rápidamente hasta ser demasiado grande para manejarlo en la práctica. +Imagina que, cada vez que se ejecuta el job, una respuesta del servidor se +agrega a `state.references` con `array.push(...)`. OpenFn admite hasta 50 000 +bytes (medidos con `byte_size` de Erlang), aunque la mayoría de los +`final_state` miden entre 100 y 1000 bytes. + +Si tu `final_state` supera los 10 000 bytes, OpenFn envía un correo de +advertencia a los colaboradores del proyecto. Si supera los 50 000 bytes, tu run +termina con éxito igualmente, pero su `final_state` no se guarda, y la próxima +vez que se ejecute ese job heredará el state final anterior, sin actualizar. (Es +decir, el último state que medía < 50 000 bytes). + +### Una solución rápida para un state final demasiado grande {#a-quick-fix-for-final-state-bloat} + +Casi siempre, un `state` final demasiado grande se debe a un mal manejo de +`state.references` o `state.data`. Para solucionarlo, limpia tu `state` final +agregando y adaptando las siguientes líneas, _ya sea_ en el callback de la +operación de tu paquete de lenguaje (si lo permite) o en una operación `fn(...)` +después de tu última operación. + +```js +fn(state => { + state.custom = somethingIntentional; + state.data = {}; + state.references = []; + return state; +}); +``` + +## Triggers de Kafka {#kafka-triggers} + +Los triggers de Kafka se eliminaron en la **v2.18.2**. Ya no se puede iniciar un +workflow consumiendo mensajes de un clúster de Kafka. + +Los triggers de Kafka existentes se convirtieron en **triggers webhook +deshabilitados** en lugar de borrarse, así que los workflows a los que +pertenecían siguen intactos. Para que uno siga funcionando, configura el sistema +que envía los datos para que apunte a la URL del webhook del trigger y +habilítalo. + +:::caution Para instalaciones de OpenFn autoalojadas + +Las variables de entorno `KAFKA_*` ya no hacen nada. Si tienes +`KAFKA_TRIGGERS_ENABLED` activado, haz una copia de seguridad y desactívalo +antes de actualizar. Si todavía necesitas Kafka, quédate en la versión anterior. + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/troubleshooting.md b/i18n/es/docusaurus-plugin-content-docs/current/build/troubleshooting.md new file mode 100644 index 000000000000..152a599bc16c --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/troubleshooting.md @@ -0,0 +1,143 @@ +--- +title: Solución de problemas en integraciones +sidebar_label: Solución de problemas +translation_source_hash: 82afbc334c9cd834780739572f9a016a72b38efa +translation_review_status: machine +--- + +Así que notaste que algo no anda del todo bien. Aquí tienes una lista de +preguntas, y de complicaciones, que pueden ayudarte a llegar al fondo del +asunto. + +:::tip + +Consulta la +[página de solución de problemas](/monitor-history/troubleshooting.md) de la +sección "Supervisar el historial" para ver consejos más específicos y errores +comunes. + +::: + + + +## La perspectiva de la implementación {#the-implementation-perspective} + +Primero, ten a mano esta lista de verificación rápida... responder estas +preguntas _en orden_ hará que dediques el menor tiempo posible a encontrar la +causa del problema, sea grande o pequeño. + +### 1. ¿Qué quieres? {#1-what-do-you-desire} + +Responder esto podría llevarte toda la vida, pero, en el contexto de la +depuración, puedes acotarlo un poco. De verdad no podemos avanzar hasta que +tengas claro lo que quieres. + +### 2. ¿Cómo lo estás pidiendo? {#2-how-are-you-asking-for-it} + +¡Muéstrame el issue, las especificaciones, el "requisito"! Asegurémonos de que +esté expresado con claridad y documentado. Si es así, ¡pasa a la pregunta 3! + +### 3. ¿Lo que pides va a producir el efecto que quieres? {#3-is-what-youre-asking-for-going-to-produce-the-effect-you-desire} + +Esta es difícil y puede requerir al equipo de ingeniería. (De hecho, suele ser +en este momento cuando se llama a ingeniería. Hay un "bug", y antes de mirar +cualquier código tenemos que averiguar si lo que se pide, la especificación, +realmente va a producir los resultados deseados). + +### 4. ¿La expresión implementa lo que pides? {#4-does-the-expression-implement-what-youre-asking-for} + +¿Estamos _seguros_ de que la especificación va a producir el efecto que +queremos? Bien, perfecto... ahora veamos la expresión del job. ¿La expresión del +job implementa la especificación? ¿Cómo puedes demostrar (con logs, aserciones, +etc.) que lo hace? No avances hasta estar seguro de esto, o seguro de que _no +puede_ hacerlo, dado el adaptor que usas. + +:::info Control de tiempo + +Nota: un cambio en la expresión del job puede llevar apenas un par de minutos. + +::: + +### 5. ¿El adaptor permite la implementación de la expresión? {#5-does-the-adaptor-supportenable-the-implementation-in-the-expression} + +Bien, si estás seguro de que la expresión hace todo lo que puede con la +especificación... ¡quizás hay un bug en el adaptor! Puede que algo en cómo se +implementó esa función auxiliar no haga lo que pretendía el autor del adaptor, y +eso podría estar produciendo el "bug". + +Si empiezas a trabajar en el adaptor, _ya_ deberías haber reducido el problema a +un **_PROBLEMA GENERAL_**, dejando de lado todos los detalles específicos de +esta implementación. Estás empezando a cambiar la forma en que este adaptor +interactúa con la API de destino. Tienes a mano la documentación de la API y +estás enviando solicitudes con cURL directamente a distintos endpoints, +configurando pruebas en el adaptor, etc. + +:::info Control de tiempo + +Un cambio en el adaptor puede llevar una hora, o quizás unas cuantas. Hablamos +del orden de un día si los cambios son grandes y cuentas el tiempo necesario +para desplegar versiones nuevas. + +::: + +### 6. ¿La API de destino permite la implementación del adaptor? {#6-does-the-target-api-supportenable-the-implementation-in-the-adaptor} + +Uf... si llegaste hasta aquí, estás en terreno "grande y serio". ¡Avanza con +cuidado! Supongo que encontraste muchos hilos de Stack Overflow que describen el +problema que tienes. Lo que estás diciendo es que, _a pesar de_ la documentación +de la API que usamos para construir este adaptor, hay algo distinto en cómo se +comporta realmente la API. + +¿Quizás hay una versión nueva de la API con un cambio incompatible? + +¿Quizás hay un bug en el sistema de destino? + +En cualquier caso, cuando llegas a este nivel estás dedicando MUCHO tiempo y te +estás involucrando con la comunidad open source en general. Deberías publicar en +al menos un foro antes de terminar el día. + +:::info Control de tiempo + +Escribir un adaptor nuevo para una versión nueva de una API, o corregir un bug +en el sistema de otro desarrollador mediante una pull request... esto lleva +semanas y meses y, peor aún, los plazos suelen estar fuera de nuestro control. + +::: + +## La perspectiva del producto {#the-product-perspective} + +Para complicar las cosas _(¡acepta la complejidad!)_, cuando me pongo el +sombrero de producto, invierto la pirámide. Aunque un problema se pueda resolver +en 15 minutos escribiendo una línea nueva en la `expression` (consulta la +pregunta 4), ¿es un problema generalizable? ¿Podría ahorrarles esos 15 minutos a +_futuros implementadores_ haciendo un cambio en el adaptor (consulta la +pregunta 5) que ofrezca esta corrección o funcionalidad "de serie"? + +Mejor aún... ¿podría hacer algún cambio en la plataforma OpenFn (o en Primero, +CommCare o DHIS2) que permitiera adaptors más fáciles o mejores y resolviera +este problema con clics, no con código? + +:::tip + +¿Recuerdas esos jobs que escribíamos que no hacían nada (simplemente devolvían +el state) si se cumplía una condición? Pues bien, con exactamente este enfoque +incorporamos a OpenFn una funcionalidad de "filtro de exclusión" que permite a +un usuario omitir ciertos mensajes entrantes según unos criterios, en lugar de +tener que evaluar esos mensajes en el job. + +Llevó mucho más trabajo que escribir ese único bloque `fn(...)` al principio del +job de un solo cliente, pero ahora le ahorra a _todo el mundo_ escribir esa +línea en el futuro. + +::: + +## Encontrar el equilibrio, al final {#find-balance-in-the-end} + +Estas preguntas siempre me dan vueltas en la cabeza e intento sopesar esta +perspectiva del producto frente a la perspectiva de la implementación. Al final, +todo es cuestión de equilibrio (nada sorprendente) en cómo _resolvemos_ estos +problemas, pero seguir la perspectiva de la implementación en cómo abordas, +entiendes, depuras y estimas pondrá más información sobre la mesa más rápido y +permitirá una mejor conversación del tipo "Bien, ¿cómo deberíamos resolver esto +dadas las limitaciones cronológicas y comerciales actuales?" entre el equipo de +implementación y el equipo de ingeniería. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/workflow-snapshots.md b/i18n/es/docusaurus-plugin-content-docs/current/build/workflow-snapshots.md new file mode 100644 index 000000000000..98b2923bd56b --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/workflow-snapshots.md @@ -0,0 +1,103 @@ +--- +title: Snapshots de workflows +sidebar_label: Snapshots de workflows +slug: /workflow-snapshots +translation_source_hash: 8a952794f200dfcaec11953aa81686bf7244d82c +translation_review_status: machine +--- + +Los snapshots de workflows capturan y guardan un estado o versión de un workflow +(la combinación de entrada, configuración del workflow y código del job) en el +momento concreto en que el workflow se actualizó o se ejecutó. Los snapshots +ayudan a depurar, auditar y mejorar el rendimiento general de los workflows. + +### ¿Cuándo se crea un snapshot? {#when-is-a-snapshot-made} + +Los snapshots se crean de 2 formas: + +1. Cuando un usuario guarda cambios en su workflow, ya sea desde el Canvas o + desde el Inspector +2. Cuando se crea un run, ya sea al crear una nueva work order o al reintentar + un run + +### ¿Cómo puedo ver un snapshot? {#how-can-i-view-a-snapshot} + +Para ver un snapshot, ve a la página `History`. Expande una work order para ver +los runs que incluye. + +![Snapshot1](/img/snapshots1.webp) + +Desde la vista expandida del historial, hay dos formas de ver los snapshots: + +1. Inspeccionando un step del run +2. Desde la vista del run + +#### Ver un snapshot inspeccionando un step del run {#viewing-a-snapshot-by-inspecting-a-step-in-the-run} + +Haz clic en el ícono de inspeccionar junto al step que quieres ver. + +![Inspect](/img/inspect.webp) + +Se abrirá la [pantalla del Inspector](/build/steps/step-editor.md) para ese step +del run, con todos sus artefactos asociados: logs y datos de entrada y salida. +Verás que el Inspector está en modo de solo lectura y, si pasas el cursor sobre +el chip con el ID del snapshot del workflow, aparecerá un mensaje que dice "You +are viewing a snapshot of this workflow that was taken on …." + +![Snapshot2](/img/snapshots2.webp) + +Para ver el Canvas correspondiente a este snapshot, cierra la vista del +Inspector haciendo clic en la `X` de la esquina superior derecha de la página. +Se abrirá el Canvas asociado, con el step seleccionado, como se muestra a +continuación. + +![Snapshot3](/img/snapshots3.webp) + +Desde el Canvas, puedes inspeccionar cualquier step haciendo clic en él y +abriendo el Inspector para el run asociado al step y al snapshot. + +#### Ver un snapshot desde la vista del run {#viewing-a-snapshot-from-the-run-view} + +Desde la vista expandida del historial, haz clic en el ID del run para abrir la +vista del run. + +![Snapshot4](/img/snapshots4.webp) + +En esta vista, haz clic en el nombre del workflow (Simple Flow) para abrir el +Canvas del workflow para este snapshot. Igual que al ver un snapshot +inspeccionando un step, puedes hacer clic en el ícono de inspeccionar junto a +los steps para abrir el Inspector del step. + +### Editar un snapshot {#editing-a-snapshot} + +Los snapshots son de solo lectura y sirven como referencia del estado de un +workflow en el momento en que se guardó o se ejecutó un run. Como solo se puede +editar la versión más reciente, para editar el workflow puedes hacer clic en +`Switch to latest version` en el Canvas o usar el interruptor de la esquina +inferior derecha de la página del Inspector para cambiar a la versión más +reciente del workflow. + +Cuando cambias a la versión más reciente, la etiqueta con el ID del snapshot se +vuelve azul y su texto pasa a ser `latest`. + +![Snapshot5](/img/snapshots5.webp) + +![Snapshot6](/img/snapshots6.webp) + +### Reintentar un snapshot {#retrying-a-snapshot} + +Cuando reintentas un run con un snapshot, el reintento se ejecuta con la versión +más reciente del workflow y del código del job. No puedes reintentar un workflow +con un snapshot anterior, solo con la versión más reciente. + +### Snapshots y control de versiones {#snapshots-and-version-control} + +Como guardan un workflow con un conjunto concreto de configuración, datos de +entrada y código del job, los snapshots son sobre todo herramientas que ayudan a +los administradores a auditar y a manejar errores (por ejemplo, para saber por +qué un caso no se actualizó correctamente en una base de datos). + +OpenFn ofrece herramientas específicas de +[control de versiones](/manage-projects/link-to-gh.md) que te permiten a ti y a +tu equipo administrar los cambios en el código de los jobs para desarrollar, +depurar y revisar de forma más rápida y segura. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/workflows-api.md b/i18n/es/docusaurus-plugin-content-docs/current/build/workflows-api.md new file mode 100644 index 000000000000..28a4268f4be0 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/workflows-api.md @@ -0,0 +1,173 @@ +--- +title: API de workflows +sidebar_label: API de workflows +translation_source_hash: 770a1a41ea82bd8249b1dfe732d7b21a8710563c +translation_review_status: machine +--- + +La API de workflows te permite crear y modificar workflows mediante código. + +Puedes usar la API de workflows con el adaptor `http` o con curl. + +:::info Compatibilidad de versiones + +La API de workflows se introdujo en la versión 2.10.10, en enero de 2025 + +::: + +## Autenticación {#authentication} + +Todas las solicitudes deben estar autenticadas. + +La autenticación usa el encabezado Authorization con un token de acceso personal +(Personal Access Token, PAT) generado en la aplicación. + +Si usas el adaptor http, asigna tu PAT al `access_token` de la credencial. + +Si usas curl, agrega el bearer token (en el ejemplo de abajo, el token se toma +de una variable de entorno): + +``` +curl -H "Authorization: Bearer $OPENFN_PAT" https://app.openfn.org/api/projects//workflows +``` + +## API REST {#rest-api} + +La API de workflows tiene la siguiente estructura RESTful: + +- `GET /api/projects/:projectId/workflows`: obtiene la lista de workflows de un + proyecto. Devuelve un array de workflows. +- `GET /api/projects/:projectId/workflows/:workflowId`: obtiene un solo workflow + por su id. Devuelve un solo workflow. +- `PUT /api/projects/:projectId/workflows`: crea un workflow nuevo. Incluye el + JSON del workflow en el cuerpo. Devuelve el JSON del workflow actualizado. +- `PUT /api/projects/:projectId/workflows/:workflowId`: actualiza un workflow. + Reemplaza el workflow existente por el JSON del cuerpo. Devuelve el JSON del + workflow actualizado. +- `PATCH /api/projects/:projectId/workflows/:workflowId`: actualiza un workflow + en parte. El workflow existente se actualiza con el JSON del cuerpo. + +## Estructura de un workflow {#workflow-structure} + +Un workflow tiene la siguiente estructura: + +``` +{ + "name": "My Workflow", + "id": "a414cb3b-e387-4c4f-b8de-70d51f1160da", + "project_id": "79efba60-072a-4d4f-8d6c-22dfd3852176", + "edges": [ + { + "id": "759fe475-ed23-4914-8a6d-155968bc0aa1" + "condition_type": "always", + "enabled": true, + "source_job_id": null, + "source_trigger_id": "c79ce46c-ab0f-4f5b-bf2d-fed52aef2a41", + "target_job_id": "26304a1e-267b-4bc9-940f-171db1905885", + } + ], + "jobs": [ + { + "id": "26304a1e-267b-4bc9-940f-171db1905885", + "body": "/* job code goes here */", + "name": "my-job", + "adaptor": "@openfn/language-common@latest", + } + ], + "triggers": [ + { + "id": "c79ce46c-ab0f-4f5b-bf2d-fed52aef2a41", + "comment": null, + "custom_path": null, + "cron_expression": null, + "type": "webhook", + "enabled": true + } + ], +} +``` + +Al crear un workflow nuevo, el servidor genera UUIDs para el workflow y para +todos sus steps y edges. Al crear nodos y edges nuevos puedes usar cualquier +cadena como id, siempre que la uses de forma coherente. + +En una solicitud PUT o PATCH, a los steps y edges nuevos se les DEBEN asignar +UUIDs. Si usas el adaptor `http`, puedes usar `util.uuid()` para hacerlo (ver el +ejemplo de abajo). + +DEBES asegurarte de que todos los steps y triggers a los que hace referencia un +edge estén definidos dentro del mismo workflow. + +## Ejemplos con el adaptor HTTP {#http-adaptor-examples} + +Tienes que crear una credencial con `access_token` igual a tu token de acceso +personal (PAT) y `baseUrl` igual a tu instancia de OpenFn (por ejemplo, +`"https://app.openfn.org"`) + +Crear un workflow nuevo: + +```js +post(`/api/projects/${$.projectId}/workflows`, { + body: { + name: 'My Workflow', + edges: [ + { + source_trigger_id: 'trigger-1', + target_job_id: 'job-1', + condition_type: 'always', + }, + ], + jobs: [ + { + id: 'job-1', + name: 'My Job', + body: '/* job code goes here */', + adaptor: '@openfn/language-common@latest', + }, + ], + triggers: [ + { + id: 'trigger-1', + type: 'webhook', + enabled: true, + }, + ], + }, + headers: { 'content-type': 'application/json' }, +}); +``` + +El workflow resultante, con los UUIDs y los metadatos actualizados, se escribe +en `state.data.workflow`. + +Agregar un step y un edge nuevos a un workflow existente: + +```js +fn(state => { + const jobId = util.uuid(); + state.diff = { + edges: [ + { + id: util.uuid(), + source_job_id: 'c79ce46c-ab0f-4f5b-bf2d-fed52aef2a41', + target_job_id: jobId, + condition_type: 'always', + }, + ], + jobs: [ + { + id: jobId, + body: '/* job code goes here */', + adaptor: '@openfn/language-common@latest', + }, + ], + }; + return state; +}); +patch(`/api/projects/${$.projectId}/workflows/${$.workflowId}`, { + body: $.diff, + headers: { 'content-type': 'application/json' }, +}); +``` + +El workflow resultante se escribe en `state.data.workflow`. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/workflows.md b/i18n/es/docusaurus-plugin-content-docs/current/build/workflows.md new file mode 100644 index 000000000000..5a0b3011a3e3 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/workflows.md @@ -0,0 +1,214 @@ +--- +title: Workflows +sidebar_label: Workflows +translation_source_hash: 13857c3ec6be5f4b35e8adf71570bb4f22318f05 +translation_review_status: machine +--- + +Los **workflows** son procesos automatizados o conjuntos de instrucciones que +cumplen una tarea. En la configuración de OpenFn, un workflow está formado por +un trigger, steps y paths que definen la lógica de la automatización. Sigue +leyendo para aprender a configurar workflows. + +## Crear workflows {#create-workflows} + +Para crear un workflow nuevo en tu proyecto: + +1. Ve a la página **Workflows**. +2. Haz clic en el botón **Create new workflow**. +3. Dale a tu workflow un `Name` descriptivo (por ejemplo, `Register patients`, + `Refer cases`, `Monthly payroll`). +4. Elige tu [trigger](/build/triggers.md) +5. Edita tu primer [step](/build/steps/steps.md) +6. Si hace falta, modifica la [condición del path](/build/paths.md) para definir + _cuándo_ debe pasar el workflow al siguiente step. +7. Configura más steps según lo necesites + +Mira el video de presentación de abajo para aprender a crear un workflow. + + + +### Unir ramas y saltar steps {#merging-branches-and-skipping-steps} + +El editor de workflows permite unir ramas y saltar steps. Para unir dos o más +steps en uno solo, o para saltar algunos steps: + +1. Pasa el cursor sobre el step que quieres unir o desde el que quieres saltar +2. Verás un ícono de más +3. Haz clic en el ícono de más y arrastra para crear un path +4. Suelta el path nuevo sobre el step que quieras de tu workflow + + + +:::note No se admiten bucles + +Los workflows con bucles no se admiten, así que tienes que conectar los paths a +steps posteriores. Cuando uses paths que unen ramas o saltan steps, puedes usar +condiciones igual que con cualquier otro step. + +::: + +## Ejecutar workflows {#run-workflows} + +Los workflows se ejecutan automáticamente cuando están "habilitados", es decir, +cuando su trigger está activado. Un trigger webhook ejecuta tu workflow cada vez +que llega una solicitud a la URL de ese trigger, y un trigger cron ejecuta un +workflow cada vez que su programación cron coincide con la hora actual. + +:::info + +Ten en cuenta que los workflows están deshabilitados de forma predeterminada. +Cuando quieras que tu workflow empiece a ejecutarse, tienes que habilitarlo +manualmente. + +::: + +### Habilitar o deshabilitar un workflow {#enabling-or-disabling-a-workflow} + +Hay dos formas de deshabilitar o habilitar un workflow en tu proyecto de OpenFn: + +1. con el interruptor de estado del workflow +2. con el trigger del workflow + +#### Con el interruptor de estado del workflow {#via-the-workflow-state-toggle} + +Puedes habilitar o deshabilitar tu workflow con el interruptor que está en su +fila de la lista de workflows del proyecto, o con el interruptor de la barra de +navegación del Canvas del workflow. + +La captura de pantalla de abajo muestra un workflow habilitado en la lista de +workflows. + +![Desde la lista de workflows](/img/workflow_list_toggle.webp) + +La captura de pantalla de abajo muestra un workflow deshabilitado en el Canvas +del workflow. + +![Desde el Canvas del workflow](/img/workflow_canvas_toggle.webp) + +#### Con el trigger del workflow {#via-the-workflow-trigger} + +Para habilitar o deshabilitar un workflow desde su trigger, selecciona el ícono +del trigger en el Canvas y usa el interruptor del panel de configuración para +cambiar el estado del workflow. + +![Workflow habilitado en el panel del trigger](/img/via-trigger-panel.webp) + +### Runs manuales {#manual-runs} + +Mira el video para ver un resumen rápido. + + + +Puedes ejecutar un workflow manualmente de tres formas: + +#### Con una entrada vacía {#with-an-empty-input} + +Es el comportamiento predeterminado, y el dataclip de entrada de tu run será +`{}`. + + + +#### Con una entrada personalizada {#with-a-custom-input} + +Puedes escribir, copiar y pegar, o importar (buscándolo en tu sistema de +archivos o arrastrándolo y soltándolo) cualquier archivo con JSON válido. + + + +#### Con una entrada existente {#with-an-existing-input} + +Puedes elegir de una lista de entradas anteriores que se usaron para ejecutar +este step. + + + +### Dataclips con nombre {#named-dataclips} + +Puedes ponerles nombre a los dataclips (entradas personalizadas, resultados de +steps, solicitudes de webhook) para encontrarlos y usarlos más fácilmente en tus +pruebas. + +:::info Los dataclips con nombre no se borran + +Los dataclips con nombre no se eliminan junto con el resto del historial del +proyecto cuando se cumple el periodo de retención. Se guardan indefinidamente. + +::: + +Para ponerle un nombre a tu dataclip, haz clic en el campo de la etiqueta. + + + +Después de ponerles nombre a tus entradas, puedes buscarlas por nombre en la +barra de búsqueda. + + + +Para ver solo las entradas con nombre, haz clic en el botón de etiqueta. + + + +## Limitar la concurrencia {#limit-concurrency} + +La **concurrencia** de un workflow es la cantidad de runs que se permiten para +ese workflow **_al mismo tiempo_**. En OpenFn, los propietarios y +administradores del proyecto pueden limitar la cantidad máxima de runs de un +workflow que se ejecutan al mismo tiempo. Puede servirte para asegurar un +procesamiento en serie, "de uno en uno", o para evitar que un workflow rápido de +OpenFn supere el límite de solicitudes de la API de otro sistema conectado. + +:::info + +Comprueba que la ejecución en paralelo no esté deshabilitada en tu proyecto, +porque eso tiene prioridad sobre el límite de concurrencia del workflow. + +::: + +### ¿Qué pasa cuando un workflow tiene un límite de concurrencia? {#what-happens-when-concurrency-limit-is-set-on-a-workflow} + +Cuando un workflow tiene configurado un límite de concurrencia, la cantidad +máxima de runs que se ejecutan a la vez no supera el número fijado para ese +workflow. Por ejemplo: + +- **Concurrencia sin configurar (o = 0)**: no se aplica ningún límite + artificial, y este workflow solo está limitado por la capacidad de cómputo + total disponible en tu instalación de OpenFn. +- **Concurrencia = 1**: los runs de este workflow se ejecutan de uno en uno. + Cada run tiene que _terminar_ antes de que empiece el siguiente. +- **Concurrencia = 2**: no se pueden ejecutar más de 2 runs de este workflow a + la vez, y los demás runs tienen que quedarse en `enqueued`. Si los runs "A", + "B" y "C" están todos en cola, empiezan a ejecutarse "A" y "B". Cuando termina + "A", empieza "C". (Nunca más de 2 a la vez). + +### Configurar la concurrencia de un workflow {#setting-concurrency-for-a-workflow} + +Los límites de concurrencia se configuran en la ventana modal de configuración +del workflow, desde el Canvas del workflow. + +1. Haz clic en el ícono de configuración, junto al botón de guardar de tu + workflow, para abrir la configuración del workflow +2. En la ventana modal, escribe el límite máximo de concurrencia +3. Haz clic en guardar. + +![Configuración de la concurrencia](/img/configuring-concurrency.webp) + +### Salida de los logs {#log-outputs} + +Por motivos de seguridad de los datos y de cumplimiento normativo, puedes +configurar la salida de los logs de un run de workflow para que no registre las +sentencias `console.log()`. Un propietario o administrador del proyecto puede +hacerlo desde la ventana modal de configuración del workflow. + +1. Haz clic en el ícono de configuración. +2. En la ventana modal, desactiva el interruptor **Allow `console.log()` usage** + para dejar de registrar las sentencias `console.log()`. Está activado de + forma predeterminada. + +![Configuración de la salida de los logs](/img/configuring-log-outputs.webp) + +## Atajos de teclado {#keyboard-shortcuts} + +Desde el Canvas puedes hacer algunas acciones comunes (por ejemplo, guardar) con +el teclado. Consulta la lista completa de atajos de teclado +[aquí](/keyboard-shortcuts.md). diff --git a/i18n/es/docusaurus-plugin-content-docs/current/build/working-with-branches.md b/i18n/es/docusaurus-plugin-content-docs/current/build/working-with-branches.md new file mode 100644 index 000000000000..67b8e41913aa --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/build/working-with-branches.md @@ -0,0 +1,63 @@ +--- +title: Gestionar cambios con ramas de GitHub +sidebar_label: Gestionar cambios +translation_source_hash: 550f3cd679d88a41a173cd21cd874d768048f718 +translation_review_status: machine +--- + +En la sección [Editar steps localmente](/build/editing-locally.md), vimos cómo +crear tus cambios y agregarlos a la rama `main` de un proyecto. + +Sin embargo, la mayoría de los cambios de código en los workflows implican +compartir y revisar los cambios antes de desplegarlos. Para hacerlo, puedes +crear, probar y compartir tus cambios en una rama nueva de GitHub y, cuando +estén listos, hacer merge en `main` para desplegarlos. + +:::tip + +Hay MUCHAS estrategias distintas para crear ramas y revisar código en Git. (¡Por +ejemplo, [GitHub Flow](https://guides.github.com/introduction/flow/) o +["ese famoso post de @nvie"](https://nvie.com/posts/a-successful-git-branching-model/)!) +Esta guía busca darte una introducción muy breve a las ramas en Git, pero no +pretende dictar la "forma correcta". + +::: + +Retomemos el proceso desde el momento en que hiciste `git pull` para traer los +últimos cambios del repositorio a tu carpeta local. + +1. Al ejecutar `git checkout -b {branch_name}`, se crea una rama nueva y te + cambias a ella. Cuando empieces a editar tus steps, los cambios se guardarán + en esta rama, separados de `main`. + +2. Para probar los cambios localmente, consulta la documentación de la + [CLI](/build-for-developers/cli-intro.md). + +3. Igual que cuando trabajas en `main`, cuando termines, revisa qué archivos + cambiaste con `git status`. + +4. Luego, usa `git add {filepath}` y después `git commit -m {change notes}` para + preparar los cambios para hacer merge en el repositorio. + +5. El siguiente comando envía tus cambios al repositorio remoto como una rama + nueva y separada: `git push --set-upstream origin {branch_name}`. + +6. En GitHub, puedes crear una pull request para que revisen y aprueben tus + cambios. + + ![PR-1](/img/pull-request.webp) + + ![PR-2](/img/pull-request-2.webp) + +7. A medida que trabajes con ramas, revisa en qué rama estás con `git status`. + +![git-status](/img/git-status.webp) + +8. Para mantener tu copia local al día con el repositorio remoto, cámbiate a + `main` con `git checkout main` y ejecuta `git pull` para traer los cambios. + +9. Si sigues trabajando en tu rama aparte mientras `main` se actualizó en el + remoto y quieres integrar esos cambios remotos, usa `git checkout main`, + luego `git pull`, luego `git checkout {working_branch_name}` y, por último, + `git merge main` para hacer merge de los cambios de `main` en tu rama de + trabajo. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/contribute/impact-tracker.md b/i18n/es/docusaurus-plugin-content-docs/current/contribute/impact-tracker.md new file mode 100644 index 000000000000..fcdf3699eecc --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/contribute/impact-tracker.md @@ -0,0 +1,77 @@ +--- +title: Rastreador de impacto +id: impact +translation_source_hash: 9cd3fac6b83ea2809ad16a7266363b379d84822d +translation_review_status: machine +--- + +## Introducción {#introduction} + +OpenFn es un bien público digital gratuito y de código abierto. Muchos usuarios +no pueden contribuir económicamente ni participar en nuestra comunidad de +desarrollo de producto, pero al enviar cada noche estos informes anónimos de uso +agregado aseguran la sostenibilidad del proyecto a largo plazo, porque: + +1. nos permiten entender las necesidades de nuestros usuarios, +2. demuestran mejor nuestro impacto, +3. y nos ayudan a mantener el apoyo de los donantes. + +:::success Rastreador de impacto anónimo + +Visita [openfn.org/impact](https://www.openfn.org/impact) para verlo en acción. + +::: + +¿Cómo funciona? Estas métricas ([ver más abajo](#the-data-yes-all-of-it)) son +anónimas y las envían operadores de instancias de todo el mundo. Cuando alguien +inicia OpenFn, lo primero que ve es un mensaje como el de abajo, que explica +exactamente qué datos anónimos agregados envía y adónde los envía. + +Según las instrucciones de instalación, los administradores de la instancia +pueden dejar de enviar métricas en cualquier momento con la variable de entorno +`USAGE_TRACKING_ENABLED`, ¡pero la mayoría prefiere contribuir! + +## Los datos. (Sí, todos.) {#the-data-yes-all-of-it} + +Si el administrador de un cliente de métricas envía datos de uso anónimos a +cualquier instancia de un servidor de métricas, esto es lo que se envía: + +```json +{ + "version": "2", + "instance": { + "version": "v2.4.2:match:f1bd9ae", + "hashed_uuid": "4CE189B993247E94FD2A9EDD28CEC9C9D5A7125AB85F4586A6C994D89DCC0979", + "no_of_users": 137, + "operating_system": "linux", + "no_of_active_users": 70 + }, + "projects": [ + { + "workflows": [ + { + "no_of_jobs": 1, + "no_of_runs": 6, + "hashed_uuid": "C08DD42A9DF75A017001429240D0E6C425BA89AF03C134A05E631CDB0A53FA87", + "no_of_steps": 6, + "no_of_active_jobs": 1 + }, + { + "no_of_jobs": 4, + "no_of_runs": 6, + "hashed_uuid": "BD70CCCA4D953D1B59F7803CC24A4EE84CFD21DE4F46CBA85FB3FA41AACA4EAD", + "no_of_steps": 24, + "no_of_active_jobs": 4 + }, + ... more workflows + ], + "hashed_uuid": "71A5B39B570E1E9156B73997C327E5A2FABD06507CE3FEBF85128016446FCD49", + "no_of_users": 6, + "no_of_active_users": 6 + }, + ... more projects + ], + "report_date": "2024-04-25", + "generated_at": "2024-04-26T01:30:00.876776Z" +} +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/contribute/roadmap.md b/i18n/es/docusaurus-plugin-content-docs/current/contribute/roadmap.md new file mode 100644 index 000000000000..cf21b78f98c8 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/contribute/roadmap.md @@ -0,0 +1,194 @@ +--- +title: Hoja de ruta y filosofía de producto +sidebar_label: Hoja de ruta +translation_source_hash: c9a9fc5e683db89572d83478f3c0327da01df9d7 +translation_review_status: machine +--- + +## Introducción {#introduction} + +Esta página detalla las hojas de ruta previstas para los productos clave de +OpenFn, incluidos Lightning, los adaptors y este sitio de documentación. Aunque +esta página se actualiza periódicamente, puedes seguir nuestra hoja de ruta y +nuestro progreso en tiempo real [aquí](#what-are-we-currently-working-on). + +## Nuestro enfoque del desarrollo de producto {#our-approach-to-product-development} + +En OpenFn seguimos el [enfoque Shape Up](https://basecamp.com/shapeup) para +ayudar a nuestro pequeño equipo de ingeniería a crear productos y funciones +_significativos_ más rápido, sin sacrificar la calidad. Con Shape Up, +normalmente nos comprometemos con _proyectos_ que se pueden entregar en un +período de 4 a 6 semanas, con varias versiones según la aprobación de QA dentro +del ciclo de desarrollo. También damos prioridad a los comentarios y las +solicitudes de funciones de nuestros usuarios por encima de las funciones del +backlog. + +:::success Transparencia y construir lo que importa + +Ten en cuenta que es bastante raro que nos comprometamos a entregar una función +concreta con más de 2 o 3 meses de antelación. Nos comprometemos a evaluar +constantemente lo que más necesitan nuestros usuarios y a dedicar los pocos +recursos que tenemos a aportarles valor; sencillamente no podemos garantizar que +lo que hoy parece la "séptima función más importante" siga en nuestra lista +dentro de 6 meses. + +Al mismo tiempo, nos esforzamos por ser lo más transparentes e inclusivos +posible en nuestros procesos de planificación. Tenemos un gran backlog de +solicitudes de funciones e issues de GitHub (reportes de bugs, borradores e +incluso épicas o proyectos parcialmente definidos) que reciben votos y +comentarios, y que nos sirven de inspiración al decidir qué priorizar a +continuación. + +¡Sigue leyendo para saber más sobre cómo trabajamos, cómo puedes ver lo que +viene y cómo puedes participar! + +::: + +## ¿En qué estamos trabajando ahora? {#what-are-we-currently-working-on} + +Todo el trabajo de nuestro equipo se sigue públicamente en un GitHub Project. +Tres vistas clave te muestran al minuto qué estamos haciendo y qué hay en +nuestra hoja de ruta inmediata. + +### Consulta [**_Now_**](https://github.com/orgs/OpenFn/projects/3/views/24?layout=table&sortedBy%5Bdirection%5D=desc&sortedBy%5BcolumnId%5D=Status) 🚧 para ver lo que se está construyendo ahora {#see-now--for-whats-currently-being-built} + +### Consulta [**_Next_**](https://github.com/orgs/OpenFn/projects/3/views/2?layout=table&sortedBy%5Bdirection%5D=desc&sortedBy%5BcolumnId%5D=Status) ⏭️ para ver lo que se está considerando para el próximo sprint {#see-next-️-for-whats-being-considered-for-the-next-sprint} + +### Consulta [**_Epics_**](https://github.com/orgs/OpenFn/projects/3/views/7) 🤔 para ver una lista de proyectos que estamos considerando, con una prioridad aproximada {#see-epics--for-a-list-of-projects-that-were-considering-roughly-prioritized} + +### Consulta [**_Bugs_**](https://github.com/orgs/OpenFn/projects/3/views/22) 🐞 para ver los bugs conocidos que estamos siguiendo {#see-bugs--for-known-bugs-were-tracking} + +Actualizaremos este sitio cada mes para reflejar nuestro progreso en los temas +principales. También puedes seguir en tiempo real todas las funciones nuevas, +cambios y correcciones de bugs en nuestro +[registro de cambios](https://openfn.github.io/lightning/changelog.html). + +## Cómo **participar** {#how-to-get-involved} + +Recopilamos comentarios y solicitudes de funciones nuevas en nuestro sitio de la +[comunidad](https://community.openfn.org/c/feature-requests/). Así, el equipo +principal de OpenFn y los usuarios pueden seguir y votar sus solicitudes +favoritas y las más críticas para su misión. + +:::info Únete a nuestra actualización semanal de producto + +Te animamos a unirte a nuestras actualizaciones semanales de producto, donde +presentamos las novedades y lo que viene. También es una buena oportunidad para +hacernos preguntas sobre OpenFn. La llamada es todos los viernes a las 11:00 GMT +(Londres) [aquí](https://meet.google.com/vaw-qvfq-mru) - +(https://meet.google.com/vaw-qvfq-mru). También puedes +[agregar nuestros eventos a tu calendario](https://calendar.google.com/calendar/u/0?cid=Y182Y2Y4NWY0NjlhNWVlMzA4NzEwMWE5MWNhYmRjZTRkMDZlZDU1OGY1OTM3ZGUzNTQ0NWNkYmQ2NDFhMDY3MGFjQGdyb3VwLmNhbGVuZGFyLmdvb2dsZS5jb20) +para no perderte nuestras demos, seminarios web y actualizaciones de producto. + +::: + +### Vota por funciones 👍 {#upvote-features-} + +1. Ve al + [tablero de solicitudes de funciones](https://community.openfn.org/c/feature-requests/) + de la comunidad +2. Desplázate hacia abajo o usa los filtros y la búsqueda para ver las + solicitudes existentes +3. Haz clic en el botón (Vote) junto al título de la solicitud para votarla +4. Si quieres más votos para esta solicitud, comparte un enlace a ella con tu + red + +### Solicita una función nueva 💡 {#request-a-new-feature-} + +1. Ve al + [tablero de solicitudes de funciones](https://community.openfn.org/c/feature-requests/) + de la comunidad +2. Haz clic en `+ New Topic` para crear una solicitud nueva. +3. Escribe un título muy claro, conciso y descriptivo para la función (por + ejemplo, "Hacer verde el botón de nuevo workflow") +4. Describe la solicitud en detalle y por qué es importante para ti. Ayuda si + puedes agregar imágenes y enlaces de referencia. +5. Comparte la solicitud en tu red profesional para conseguir votos + +:::info Consejo + +Al describir la función, nos ayuda mucho que expliques el problema, la solución +propuesta (si la hay) y soluciones similares de las que podamos sacar ideas, _si +existen_. + +::: + +### Abre un issue o reporta un bug directamente {#open-an-issue-or-bug-directly} + +Si prefieres ir directo, puedes buscar entre todos los issues que seguimos en la +organización de GitHub de OpenFn [aquí](https://github.com/OpenFn), comentarlos +o incluso encargarte de ellos. Si no encuentras lo que buscas, crea un issue en +el repositorio correspondiente. ¡Haremos lo posible por responder pronto! + + + +## ¿Tienes preguntas o comentarios, o encontraste un bug? {#have-questions-feedback-or-found-a-bug} + +Te animamos a publicar tus preguntas en la comunidad de OpenFn en +[community.openfn.org](https://community.openfn.org), o a crear issues para los +bugs en el repositorio del producto. También puedes empezar a contribuir por tu +cuenta al software, los adaptors o la documentación de OpenFn +[aquí](/contribute/writing-code.md). diff --git a/i18n/es/docusaurus-plugin-content-docs/current/contribute/style-guide.md b/i18n/es/docusaurus-plugin-content-docs/current/contribute/style-guide.md new file mode 100644 index 000000000000..9558ef6ed3da --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/contribute/style-guide.md @@ -0,0 +1,262 @@ +--- +id: style-guide +title: Guía de estilo +sidebar_label: Guía de estilo +slug: /style-guide +translation_source_hash: 18fab29195b8958e596ca045c0f84dab79b66de4 +translation_review_status: machine +--- + +Puedes escribir contenido con la +[sintaxis de Markdown de GitHub](https://github.github.com/gfm/). + +:::tip + +Usamos un archivo `.prettierrc` para aplicar estilos estándar con el formateador +de código "Prettier". Si usas VS Code, puedes instalar Prettier desde +https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode + +Asegúrate de formatear tu trabajo antes de abrir una PR. + +::: + +## Sintaxis de Markdown {#markdown-syntax} + +Sirve como página de ejemplo para dar estilo a sitios de Docusaurus basados en +Markdown. + +## Encabezados {#headers} + +# H1 - Crea la mejor documentación {#h1---create-the-best-documentation} + +## H2 - Crea la mejor documentación {#h2---create-the-best-documentation} + +### H3 - Crea la mejor documentación {#h3---create-the-best-documentation} + +#### H4 - Crea la mejor documentación {#h4---create-the-best-documentation} + +##### H5 - Crea la mejor documentación {#h5---create-the-best-documentation} + +###### H6 - Crea la mejor documentación {#h6---create-the-best-documentation} + +--- + +## Énfasis {#emphasis} + +El énfasis, también llamado cursiva, con _asteriscos_ o _guiones bajos_. + +El énfasis fuerte, también llamado negrita, con **asteriscos** o **guiones +bajos**. + +Énfasis combinado con **asteriscos y _guiones bajos_**. + +El tachado usa dos virgulillas. ~~Tacha esto.~~ + +--- + +## Listas {#lists} + +1. Primer elemento de la lista ordenada +1. Otro elemento + - Sublista sin orden. +1. Los números reales no importan, solo que sea un número + 1. Sublista ordenada +1. Y otro elemento. + +- La lista sin orden puede usar asteriscos + +* O guiones + +- O signos más + +--- + +## Enlaces {#links} + +[Soy un enlace en línea](https://www.google.com/) + +[Soy un enlace en línea con título](https://www.google.com/ 'Página de inicio de Google') + +[Soy un enlace de +referencia][texto de referencia arbitrario sin distinción de mayúsculas] + +[Puedes usar números para definir enlaces de referencia][1] + +O déjalo vacío y usa el [propio texto del enlace]. + +Las URL, con o sin corchetes angulares, se convierten automáticamente en +enlaces. http://www.example.com/ o <http://www.example.com/> y a veces +example.com (pero no en GitHub, por ejemplo). + +Algo de texto para mostrar que los enlaces de referencia pueden ir más adelante. + +[texto de referencia arbitrario sin distinción de mayúsculas]: + https://www.mozilla.org/ +[1]: http://slashdot.org/ +[propio texto del enlace]: http://www.reddit.com/ + +--- + +## Imágenes {#images} + +Este es nuestro logo (pasa el cursor por encima para ver el texto del título): + +En línea: +![texto alternativo](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Texto del título del logo 1') + +De referencia: ![texto alternativo][logo] + + +[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png + 'Texto del título del logo 2' + + +Puedes usar imágenes de cualquier carpeta indicando la ruta al archivo. La ruta +debe ser relativa al archivo Markdown. + +![img](/img/undraw_Portfolio_update_re_jqnp.svg) + +### Tamaño y estilo de las imágenes {#image-sizingstyling} + +Puedes cambiar el tamaño de las imágenes con HTML en línea. + + + +--- + +## GIF {#gifs} + +Los GIF son útiles para mostrar secuencias cortas de acciones del usuario. + +![img](/img/how-to-gif.gif) + +Hay muchas herramientas que te ayudan a crear GIF: + +- [Peek](https://github.com/phw/peek) +- [Capture to a Gif](https://chrome.google.com/webstore/detail/capture-to-a-gif/eapecadlmfblmnfnojebefkbginhggeh) +- [Chrome Capture](https://chrome.google.com/webstore/detail/chrome-capture-screenshot/ggaabchcecdbomdcnbahdfddfikjmphe) + +:::note + +Si usas un "punto de cursor" animado y una "animación al mostrar o hacer clic", +el código hexadecimal que usamos es **#B53F48**. + +::: + +--- + +## Código {#code} + +```javascript +var s = 'JavaScript syntax highlighting'; +alert(s); +``` + +```python +s = "Python syntax highlighting" +print(s) +``` + +``` +No language indicated, so no syntax highlighting. +But let's throw in a tag. +``` + +```js {2} +function highlightMe() { + console.log('This line can be highlighted!'); +} +``` + +--- + +## Tablas {#tables} + +Puedes usar dos puntos para alinear las columnas. + +| Las tablas | son | geniales | +| ------------------ | :-------------------: | -------: | +| la col 3 está | alineada a la derecha | \$1600 | +| la col 2 está | centrada | \$12 | +| las rayas de cebra | quedan bien | \$1 | + +Debe haber al menos 3 guiones separando cada celda del encabezado. Las barras +verticales exteriores (|) son opcionales, y no hace falta que el Markdown sin +procesar quede bien alineado. También puedes usar Markdown en línea. + +| Markdown | menos | bonito | +| -------- | --------- | -------- | +| _Aún_ | `renders` | **bien** | +| 1 | 2 | 3 | + +--- + +## Bloques de cita {#blockquotes} + +> Los bloques de cita son muy útiles en el correo electrónico para imitar el +> texto de respuesta. Esta línea forma parte de la misma cita. + +Corte de cita. + +> Esta es una línea muy larga que se seguirá citando correctamente cuando se +> ajuste. Vaya, sigamos escribiendo para asegurarnos de que sea lo bastante +> larga como para que se ajuste en todas las pantallas. Ah, y puedes _poner_ +> **Markdown** en un bloque de cita. + +--- + +## HTML en línea {#inline-html} + +
+
Lista de definiciones
+
Es algo que la gente usa a veces.
+ +
Markdown en HTML
+
*No* funciona **muy** bien. Usa etiquetas HTML.
+
+ +--- + +## Saltos de línea {#line-breaks} + +Esta es una línea para empezar. + +Esta línea está separada de la anterior por dos saltos de línea, así que será un +_párrafo aparte_. + +Esta línea también es un párrafo aparte, pero... Esta línea está separada solo +por un salto de línea, así que es una línea aparte en el _mismo párrafo_. + +--- + +## Avisos {#admonitions} + +:::note + +Esto es una nota + +::: + +:::tip + +Esto es un consejo + +::: + +:::important + +Esto es importante + +::: + +:::caution + +Esto es una precaución + +::: + +:::warning + +Esto es una advertencia + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/contribute/writing-code.md b/i18n/es/docusaurus-plugin-content-docs/current/contribute/writing-code.md new file mode 100644 index 000000000000..ae291972649a --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/contribute/writing-code.md @@ -0,0 +1,34 @@ +--- +title: Escribir código +translation_source_hash: 71b48081ff9089f5e2dd4f5125dc7ebc37e6fbd0 +translation_review_status: machine +--- + +## Introducción {#introduction} + +Esta sección ofrece a los desarrolladores una introducción básica para +contribuir a las aplicaciones de código abierto de OpenFn. + +Hay tres formas de contribuir a OpenFn, el bien público digital (DPG): + +### 1. Crea o amplía adaptors de OpenFn {#1-build-or-extend-openfn-adaptors} + +- Requiere conocimientos de JavaScript y TypeScript +- Consulta el [README.md](https://github.com/OpenFn/adaptors#contributing) para + saber cómo contribuir + +### 2. Agrega o mejora una funcionalidad de la plataforma OpenFn Lightning {#2-add-or-improve-a-feature-on-the-openfn-lightning-platform} + +- Requiere conocimientos de Elixir y Phoenix LiveView +- Consulta el + [README.md](https://github.com/OpenFn/lightning#contribute-to-this-project) + para saber cómo contribuir + +### 3. Amplía o mejora nuestra documentación {#3-add-to-or-improve-our-documentation} + +No dudes en señalar [issues](https://github.com/openfn/docs/issues) en la +documentación de OpenFn o, si no encuentras el repositorio adecuado, problemas +con las propias herramientas. (¡Cuantos más comentarios, mejor!) Si quieres +proponer texto nuevo para la documentación, ¡puedes hacer esos cambios con el +enlace **"Editar esta página"** al final de cualquier página y enviando una pull +request! diff --git a/i18n/es/docusaurus-plugin-content-docs/current/contribute/writing-docs.md b/i18n/es/docusaurus-plugin-content-docs/current/contribute/writing-docs.md new file mode 100644 index 000000000000..3ca3793ba3ea --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/contribute/writing-docs.md @@ -0,0 +1,67 @@ +--- +title: Escribir documentación +translation_source_hash: 9c0b7e14cd0181c531460200f8f126ea0a273c80 +translation_review_status: machine +--- + +No dudes en señalar [issues](https://github.com/openfn/docs/issues) en esta +documentación o, si no encuentras el repositorio adecuado, problemas con las +propias herramientas. (¡Cuantos más comentarios, mejor!) Si quieres proponer +texto nuevo para la documentación, ¡puedes hacer esos cambios con el enlace +**"Editar esta página"** al final de cualquier página y enviando una pull +request! + +## Introducción {#intro} + +Este documento es una guía para la documentación de OpenFn. Recuerda que el +objetivo es tratar "la documentación como código" y crear un portal de +documentación que permita usar las herramientas de OpenFn de forma bastante +autónoma. Puedes contribuir a este documento. + +## Qué es la documentación {#what-are-docs} + +Cuando hablamos de documentación, nos referimos a información concisa, precisa y +que evoluciona rápido, que ayuda a los integradores ciudadanos que usan OpenFn a +entender las interfaces complejas de la plataforma. ¿Qué significa tratar la +documentación como código? Guardar los archivos fuente en un sistema de control +de versiones. Generar los artefactos de la documentación automáticamente. +Asegurarse de que un grupo de revisores de confianza revise la documentación +minuciosamente. Publicar los artefactos con poca intervención humana. + +(Fuente: el libro de Anne Gentle +_[Docs Like Code](https://www.docslikecode.com/about/)_.) + +## Objetivos de esta documentación {#goals-for-these-docs} + +### Promover la colaboración {#promote-collaboration} + +Colabora con los contribuidores de forma eficiente: mantén la documentación en +el mismo sistema que el código y genera los entregables a partir de archivos +fuente. + +### Conseguir contribuciones de cola larga {#get-long-tail-contributions} + +Divide los entregables en partes que animen a hacer contribuciones pequeñas pero +valiosas. Ya no hace falta que una sola persona se encargue de todo un +entregable de documentación. + +### Seguir los bugs de la documentación como los bugs del código {#track-doc-bugs-like-code-bugs} + +Cuando corrijas un bug de la documentación, menciona ese bug en el mensaje del +commit para que los revisores puedan juzgar si la corrección resuelve el +problema planteado. + +### Conseguir revisiones rápidas y de calidad de los miembros del equipo {#get-prompt-and-good-quality-reviews-from-team-members} + +Confía en que los miembros del equipo valoren la documentación, garanticen la +precisión técnica y la coherencia, respeten las necesidades de los usuarios +finales y defiendan los mejores entregables de documentación para sus lectores. + +### Hacer documentación atractiva {#make-beautiful-docs} + +El diseño es importante. Crea documentación atractiva y moderna. + +### Usar herramientas y flujos de trabajo de desarrollo {#use-developer-tools-and-workflows} + +Automatiza el proceso lo más posible para que podamos centrarnos en crear +contenido. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/deploy/options.md b/i18n/es/docusaurus-plugin-content-docs/current/deploy/options.md new file mode 100644 index 000000000000..066dd2587e92 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/deploy/options.md @@ -0,0 +1,119 @@ +--- +title: Planificación +translation_source_hash: 0bf6b304d3c23534195b5a7bdc18065b53aec01c +translation_review_status: machine +--- + +## Introducción {#introduction} + +Puedes usar OpenFn como un servicio en la nube seguro, estable y escalable, o +desplegarlo localmente, con opciones administradas y no administradas. Sea cual +sea el camino que elijas, puedes configurar OpenFn para que ningún dato sensible +se guarde fuera de las fronteras de tu país. + +:::success Portabilidad + +Gracias a la [especificación de portabilidad](/deploy/portability.md) de OpenFn +y a sus herramientas de despliegue de código abierto, puedes pasar de una de +estas opciones a otra en cualquier momento. Nos comprometemos a que no dependas +de ningún proveedor (**no vendor lock-in**). + +::: + +| Opción | Nube gratuita | OpenFn Cloud | Dedicada | Hazlo tú mismo (DIY) | +| :-------------------------: | :-------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| Descripción | Pasa a producción hoy mismo en OpenFn.org con proyectos de pequeña escala | Aumenta o reduce la escala y paga solo lo que necesitas | Una instalación de OpenFn dedicada y sin restricciones en cualquier parte del mundo, en nuestros servidores o en los tuyos | Despliega y administra tus propias soluciones con OpenFn | +| Licencia | Gratis para siempre, con límites de uso | **SaaS** ([planes](https://www.openfn.org/pricing)); contacta a enterprise@openfn.org para acuerdos personalizados o con factura | **SDaaS** incluye como servicio el despliegue, el mantenimiento, los parches de seguridad, las actualizaciones y la resolución de problemas; contacta a enterprise@openfn.org | La licencia LGPLv3 te permite usarlo libremente en cualquier solución cerrada o de código abierto, pero todas las obras _derivadas_ tienen que ser de código abierto | +| Ubicación | Infraestructura en la nube **global** y segura | Infraestructura en la nube **global** y segura | Infraestructura **local (en el país)** o **global** | Donde quieras | +| Despliegue | **Haz clic para empezar** en [OpenFn.org](https://www.openfn.org/signup) | **Haz clic para empezar** en [OpenFn.org](https://www.openfn.org/signup) | **Contacta a** enterprise@openfn.org | Lee esta página de la documentación y visita nuestro [GitHub](https://www.github.com/OpenFn) | +| Instalación y configuración | **Tú eliges**: configurarlo por tu cuenta, con un implementador certificado o con el equipo de OpenFn.org | **Tú eliges**: configurarlo por tu cuenta, con un implementador certificado o con el equipo de OpenFn.org | **Tú eliges**: configurarlo por tu cuenta, con un implementador certificado o con el equipo de OpenFn.org | **Tú eliges**: configurarlo por tu cuenta, con un implementador certificado o con el equipo de OpenFn.org | +| Soporte | Da y recibe soporte a través de la [comunidad](https://community.openfn.org) | Varios niveles a través de support@openfn.org | Varios niveles a través de support@openfn.org | Da y recibe soporte a través de la [comunidad](https://community.openfn.org) | + +## Ejemplo de plan de despliegue local {#sample-local-deployment-plan} + +:::info Esto es solo un ejemplo + +Tus requisitos serán distintos, pero este es un ejemplo de plan para lograr un +despliegue local a gran escala y con datos muy sensibles. + +::: + +Si estás considerando una implementación de OpenFn a gran escala o con datos muy +sensibles en servidores locales o administrados por el gobierno, podrías: + +1. **Ejecutar una prueba de concepto, un prototipo o una solución en producción + por tiempo limitado** con el servicio en la nube mientras determinas si se + ajusta a tus necesidades y qué valor aporta. (Es una forma más segura, más + barata y más rápida de demostrar el valor y la viabilidad de la solución en + sí). +2. Mientras se ejecuta la primera fase, **evaluar el valor y empezar los + preparativos**: + 1. Evalúa el **valor de la solución** en sí: ¿resuelve los problemas que + esperabas? + 2. Evalúa tus **requisitos de residencia de datos**: ¿necesitas ejecutar esta + solución en el país? + 3. Evalúa la **capacidad de DevOps** de tu equipo: ¿cómo van otros + despliegues locales de bienes públicos digitales (DPG)? + 4. Evalúa la infraestructura de cómputo, almacenamiento y redes de tu país: + ¿qué opciones\* hay disponibles para servidores y conectividad de red? + 5. Determina si lo mejor para tu ministerio es una solución en la nube de + **"persistencia cero"** o una solución **desplegada localmente**: con los + datos anteriores, haz un análisis de costo-beneficio de ambas opciones. +3. Trabajar con OpenFn.org o con un socio certificado para **practicar el + despliegue**, la migración, la reversión, el reinicio, las copias de + seguridad, etc. +4. Con las herramientas de portabilidad de OpenFn, **ejecutar una copia local** + de tu solución alojada en la nube para evaluar si tu despliegue local está + listo. +5. Establecer con OpenFn un **protocolo de conmutación por error** para "pasar a + la nube" en el caso de los sistemas críticos. + 1. ¿Con qué frecuencia se debería respaldar la configuración de la + implementación (no los datos sensibles) en la nube alojada por OpenFn.org? + 2. ¿A qué credenciales o entornos de prueba debería tener acceso la copia de + seguridad en la nube? + 3. Establece un plan para cambiar entre la nube y el despliegue local. +6. Establecer un **contrato de soporte** con proveedores locales certificados + por OpenFn o con el equipo principal de OpenFn para que te ayuden a mantener + el despliegue local si surgen problemas. +7. **Pasar por completo a tu despliegue local** y mantener la capacidad de dar + soporte a tu solución o de volver a desplegarla en otros servidores en la + nube o locales. +8. **Supervisar y ajustar tu estrategia** cuando haga falta, a medida que + evolucionan los requisitos de uso y de soberanía de datos de tu país. + +\*Visita la página [Requisitos](/deploy/requirements.md) para obtener más +información sobre las especificaciones de servidor recomendadas. + +## Pasar de la nube a un despliegue local (v1 o v2) {#moving-from-cloud-to-local-v1-or-v2} + +Si planeas una implementación autoalojada, te recomendamos desarrollar y probar +la solución inicial en el SaaS de OpenFn (v1 o v2, quizás en un plan gratuito) y +luego exportarla para usarla en Lightning (v2). + +Así, el implementador puede concentrarse en resolver los requisitos de negocio y +técnicos de la automatización antes de asumir los costos del despliegue. +Concéntrate en la solución, no en el despliegue. Después, cuando hayas hecho el +piloto, hayas demostrado su valor y estés listo para escalarla, puedes migrar tu +solución de OpenFn a un despliegue local de Lightning. + +### Recorrido de un usuario de OpenFn desplegado localmente {#a-user-journey-for-locally-deployed-openfn} + +1. Crea y prueba tus workflows en [OpenFn.org](https://www.openfn.org). +2. Exporta tu proyecto de OpenFn _como código_ con el botón "export" o con la + CLI de despliegue. +3. Despliega tu instancia local de OpenFn/Lightning. +4. Importa tu proyecto (del paso 2) a tu instancia local de OpenFn/Lightning con + la CLI de despliegue. +5. Vuelve a configurar tus credenciales (los secretos de las credenciales _no_ + se incluyen en la exportación). +6. Prueba tu proyecto desplegado localmente. + +## Guías técnicas {#technical-guidelines} + +Para ver la documentación detallada sobre el despliegue, visita la +[página de documentación para desarrolladores](https://openfn.github.io/lightning/readme.html) +de Lightning y presta especial atención a estas secciones: + +1. [Getting Started](https://openfn.github.io/lightning/readme.html#getting-started) +2. [Deployment Considerations](https://openfn.github.io/lightning/deployment.html) +3. [Benchmarking](https://openfn.github.io/lightning/benchmarking.md.html#run-benchmarking-tests-against-the-demo-webhook) diff --git a/i18n/es/docusaurus-plugin-content-docs/current/deploy/portability-v3.md b/i18n/es/docusaurus-plugin-content-docs/current/deploy/portability-v3.md new file mode 100644 index 000000000000..0b7e481bcec5 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/deploy/portability-v3.md @@ -0,0 +1,447 @@ +--- +title: Portabilidad v3 (versión anterior) +translation_source_hash: 8c147d873969c77e43aa0cfd51b29ee7ae97a0ec +translation_review_status: machine +--- + +La especificación de portabilidad permite representar proyectos de workflows +completos "como código", permite a los usuarios moverse entre distintas formas +de despliegue (como en la nube, local o alojado) y propone una forma de definir +reglas de automatización de workflows y de integración de sistemas aplicable a +nivel global, que se podría usar en todos los motores de workflows y plataformas +de integración del sector. + +Nada en la especificación _tiene_ que ser exclusivo de OpenFn ni de ninguno de +nuestros productos. Imaginamos un futuro en el que el software creado con +Lightning, el OpenFn Integration Toolkit y herramientas de integración o de +workflows completamente nuevas y distintas puedan adoptar esta especificación. + +Si te interesa contribuir a la especificación, contacta a OpenFn a través del +[foro de la comunidad](https://community.openfn.org), escríbenos o sugiere +cambios enviando una pull request aquí. + +:::warning + +Esta es la versión anterior de la especificación de portabilidad de OpenFn. + +Para ver la versión más reciente, consulta [Portabilidad](portability) + +::: + +## Proyectos "como código" {#projects-as-code} + +Los proyectos completos (grupos de workflows con sus triggers, edges, +credenciales y jobs) se pueden representar como código. + +Esto mejora la experiencia de desarrollo en OpenFn porque (a) permite crear y +probar workflows localmente; (b) permite el control de versiones de los +proyectos y un registro de auditoría de sus cambios; y (c) permite a los +usuarios trasladar proyectos existentes entre distintas instancias (es decir, +despliegues) de Lightning. + +### Estructura de directorios {#directory-structure} + +Muchos usuarios guardan sus proyectos de OpenFn en repositorios de git, y esta +es una estructura habitual: + +``` +myProject/ +├── workflow-a/ +│ ├── job-1.js +│ ├── job-2.js +│ └── job-3.js +├── workflow-b/ +│ └── job-4.js +├── project.yaml +├── projectState.json +└── config.json +``` + +:::info Estructura de directorios + +Hay 3 estructuras de directorios que se suelen usar en los proyectos de OpenFn: +estándar, producción y prueba, y monorepo. + +::: + +### La **_especificación_** del proyecto {#the-project-spec} + +La especificación del proyecto (o "spec") se suele guardar en un archivo +`project.yaml`. Aunque la mayor parte de la especificación se escribe +directamente en el archivo, muchos desarrolladores prefieren guardar el cuerpo +de sus jobs en archivos `.js` aparte y hacer referencia a ellos con una ruta +relativa. + +```yaml +name: openhie-project +description: Some sample +credentials: + jane-smith@test.com-HAPI-FHIR: + owner: jane-smith@test.com + name: HAPI FHIR +workflows: + OpenHIE-Workflow: + name: OpenHIE Workflow + jobs: + FHIR-standard-Data-with-change: + name: FHIR-standard-Data-with-change + adaptor: '@openfn/language-http@latest' + enabled: true + credential: null + body: + path: ./jobs/my-fancy-script.js + + Send-to-OpenHIM-to-route-to-SHR: + name: Send-to-OpenHIM-to-route-to-SHR + adaptor: '@openfn/language-http@latest' + enabled: true + credential: jane-smith@test.com-HAPI-FHIR + body: | + fn(state => { + console.log("hello github integration") + return state + }); + + Notify-CHW-upload-successful: + name: Notify-CHW-upload-successful + adaptor: '@openfn/language-http@latest' + enabled: true + credential: null + body: fn(state => state); + + Notify-CHW-upload-failed: + name: Notify-CHW-upload-failed + adaptor: '@openfn/language-http@latest' + enabled: true + credential: null + body: + path: ./jobs/notify-failure.js + + triggers: + webhook: + type: webhook + edges: + webhook->FHIR-standard-Data-with-change: + source_trigger: webhook + target_job: FHIR-standard-Data-with-change + condition: always + FHIR-standard-Data-with-change->Send-to-OpenHIM-to-route-to-SHR: + source_job: FHIR-standard-Data-with-change + target_job: Send-to-OpenHIM-to-route-to-SHR + condition: on_job_success + Send-to-OpenHIM-to-route-to-SHR->Notify-CHW-upload-successful: + source_job: Send-to-OpenHIM-to-route-to-SHR + target_job: Notify-CHW-upload-successful + condition: on_job_success + Send-to-OpenHIM-to-route-to-SHR->Notify-CHW-upload-failed: + source_job: Send-to-OpenHIM-to-route-to-SHR + target_job: Notify-CHW-upload-failed + condition: on_job_failure +``` + +En esta especificación puedes ver las distintas formas de definir el cuerpo de +un job: + +1. Cuerpo en línea: se usa en los jobs `FHIR-standard-Data-with-change` y + `Send-to-OpenHIM-to-route-to-SHR`. El cuerpo se escribe directamente en el + archivo YAML. + +2. Referencia a un archivo externo: se usa en los jobs + `Notify-CHW-upload-successful` y `Notify-CHW-upload-failed`. El cuerpo se + guarda en archivos aparte, a los que se hace referencia con la clave path. + Así se puede organizar mejor la lógica de los jobs complejos. + +Al usar rutas de archivo: + +- Las rutas son relativas a la ubicación del archivo `project.yaml`. +- Asegúrate de que los archivos a los que haces referencia existen y contienen + código válido para el cuerpo de un job. +- Este método es especialmente útil para jobs complejos o cuando quieres + reutilizar el cuerpo de un job en distintos proyectos. + +### El **_estado_** del proyecto {#the-project-state} + +El estado del proyecto es una representación de un proyecto concreto tal como +está _en una instancia específica de Lightning_. Se suele guardar como +`projectState.json` y contiene los UUID de los recursos en un despliegue +concreto de Lightning. + +```json +{ + "id": "8deff39d-8189-4bd7-9dc7-f9f08e7f2c60", + "name": "openhie-project", + "description": null, + "inserted_at": "2023-08-25T08:57:31", + "updated_at": "2023-08-25T08:57:31", + "scheduled_deletion": null, + "requires_mfa": false, + "project_credentials": { + "jane-smith@test.com-HAPI-FHIR": { + "id": "25f48989-d349-4eb8-99c3-923ebba5b116", + "name": "HAPI FHIR", + "owner": "jane-smith@test.com" + } + }, + "workflows": { + "OpenHIE-Workflow": { + "id": "27ae2937-0959-48b8-a597-b1646aae8c14", + "name": "OpenHIE Workflow", + "jobs": { + "Transform-data-to-FHIR-standard": { + "id": "e44f65bb-5038-4e17-8d93-b63cbe95254a", + "delete": true + }, + "Send-to-OpenHIM-to-route-to-SHR": { + "id": "977b87ff-f347-42b5-832f-6ae2ca726f32", + "name": "Send-to-OpenHIM-to-route-to-SHR", + "adaptor": "@openfn/language-http@latest", + "body": "fn(state => state);\n", + "enabled": true + }, + "Notify-CHW-upload-successful": { + "id": "86b743a3-fd00-4629-b9fb-d5f38fb56d0b", + "name": "Notify-CHW-upload-successful", + "adaptor": "@openfn/language-http@latest", + "body": "fn(state => state);\n", + "enabled": true + }, + "Notify-CHW-upload-failed": { + "id": "be85df30-0abd-4f8e-be17-501f67e18b8d", + "name": "Notify-CHW-upload-failed", + "adaptor": "@openfn/language-http@latest", + "body": "fn(state => state);\n", + "enabled": true + }, + "FHIR-standard-Data": { + "id": "55016dda-42e3-4ee1-8a9c-24e3f23d42f1", + "delete": true + }, + "FHIR-standard-Data-with-change": { + "id": "28dd0846-a6ae-40c0-8ab4-3e0a6b487afe", + "name": "FHIR-standard-Data-with-change", + "adaptor": "@openfn/language-http@latest", + "body": "fn(state => state);\n", + "enabled": true + } + }, + "triggers": { + "webhook": { + "id": "530cde0b-0de4-4f68-8834-0a4356a2fe53", + "type": "webhook" + } + }, + "edges": { + "webhook->Transform-data-to-FHIR-standard": { + "id": "b2c7407b-0ae9-4ca5-9d6b-ee624976fa54", + "delete": true + }, + "Transform-data-to-FHIR-standard->Send-to-OpenHIM-to-route-to-SHR": { + "id": "d22ed6f4-26a2-4c85-b261-cc110a6851e6", + "delete": true + }, + "Send-to-OpenHIM-to-route-to-SHR->Notify-CHW-upload-successful": { + "id": "26c12f7f-7806-4008-87cd-6747998f95f4", + "condition": "on_job_success", + "source_job_id": "977b87ff-f347-42b5-832f-6ae2ca726f32", + "source_trigger_id": null, + "target_job_id": "86b743a3-fd00-4629-b9fb-d5f38fb56d0b" + }, + "Send-to-OpenHIM-to-route-to-SHR->Notify-CHW-upload-failed": { + "id": "0630ac96-4f67-4de7-8c3d-0bf3f89f80d9", + "condition": "on_job_failure", + "source_job_id": "977b87ff-f347-42b5-832f-6ae2ca726f32", + "source_trigger_id": null, + "target_job_id": "be85df30-0abd-4f8e-be17-501f67e18b8d" + }, + "webhook->FHIR-standard-Data": { + "id": "5ce3a8ed-b9eb-464a-a2cd-ba55adc393c2", + "delete": true + }, + "FHIR-standard-Data->Send-to-OpenHIM-to-route-to-SHR": { + "id": "5f459cd9-2882-4a61-a2cc-ec45e58d4837", + "delete": true + }, + "webhook->FHIR-standard-Data-with-change": { + "id": "75e7f7d8-274b-410d-9600-730bbd535229", + "condition": "always", + "source_job_id": null, + "source_trigger_id": "530cde0b-0de4-4f68-8834-0a4356a2fe53", + "target_job_id": "28dd0846-a6ae-40c0-8ab4-3e0a6b487afe" + }, + "FHIR-standard-Data-with-change->Send-to-OpenHIM-to-route-to-SHR": { + "id": "1e5ba385-2c49-4241-8cd2-042c99a810ec", + "condition": "on_job_success", + "source_job_id": "28dd0846-a6ae-40c0-8ab4-3e0a6b487afe", + "source_trigger_id": null, + "target_job_id": "977b87ff-f347-42b5-832f-6ae2ca726f32" + } + } + } + } +} +``` + +## Usar la CLI para interactuar con proyectos {#using-the-cli-interact-with-projects} + +La especificación y el estado de un proyecto se pueden usar con distintos fines. +Por ejemplo, puedes generar el estado y la especificación como copias de +seguridad del proyecto, o generar estos archivos y usarlos para auditorías y +registros. La [CLI](https://github.com/OpenFn/kit/tree/main/packages/cli) de +OpenFn incluye comandos para descargar la configuración de un proyecto desde un +servidor de Lightning en ejecución, y para desplegar o enviar cambios a +proyectos existentes en un servidor de Lightning. Para saber más sobre el +control de versiones automatizado con pull y deploy, consulta nuestra +documentación sobre [control de versiones](/manage-projects/link-to-gh.md). + +:::info ¿Todavía no tienes la CLI? + +Instálala ejecutando `npm install -g @openfn/cli` + +::: + +Antes de usar la CLI, configúrala con variables de entorno: + +``` +OPENFN_ENDPOINT=https://app.openfn.org +OPENFN_API_KEY=yourSecretApiToken +``` + +O con un archivo `config.json`: + +```json +{ + // Required, can be overridden or set with `OPENFN_API_KEY` env var + "apiKey": "***", + + // Optional: can be set using the -p, defaults to project.yaml + "specPath": "project.yaml", + + // Optional: can be set using -s, defaults to .state.json + "statePath": ".state.json", + + // Optional: defaults to OpenFn.org's API, can be overridden or set with + // `OPENFN_ENDPOINT` env var + "endpoint": "https://app.openfn.org" +} +``` + +Puedes encontrar más detalles sobre la CLI +[aquí](https://github.com/OpenFn/kit/tree/main/packages/cli#basic-usage). + +### `openfn pull` para generar la especificación y el estado {#openfn-pull-to-generate-spec--state} + +Para generar los archivos de especificación y de estado de un proyecto +existente, usa: + +```sh +openfn pull {YOUR-PROJECT-UUID} -c ./config.json +``` + +Este comando guarda (o sobrescribe) un archivo de especificación y uno de estado +del proyecto según la ruta que hayas definido en tu configuración. + +### `openfn deploy` para crear proyectos nuevos {#openfn-deploy-to-create-new-projects} + +Para desplegar un proyecto nuevo en una instancia de Lightning a partir de un +archivo de especificación (sin un archivo de estado), usa: + +```sh +openfn deploy -c config.json +``` + +### `openfn deploy` para actualizar proyectos existentes {#openfn-deploy-to-update-existing-projects} + +Con un estado de proyecto válido definido en tu `config.json`, el mismo comando +`openfn deploy` envía tus cambios según la diferencia entre la especificación de +tu proyecto y lo que hay en el servidor. + +```sh +openfn deploy -c config.json +Checking https://demo.openfn.org/api/provision/4adf2644-ed4e-4f97-a24c-ab35b3cb1efa for existing project. +Project found. +[CLI] ♦ Changes: + { + workflows: [ + { + jobs: [ + { +- body: "fn(state => {\n console.log(\"ok\")\n return state\n});" ++ body: "fn(state => {\n console.log(\"some changes here!\")\n return state\n});\n" + } + ... + ... + ... + ] + } + ] + } + +? Deploy? yes +[CLI] ♦ Deployed. +``` + +## Obtener ayuda con la CLI {#getting-help-with-the-cli} + +El paquete de la CLI incluye una ayuda integrada (`help`). Si agregas `--help` a +un comando, como `openfn deploy --help`, verás un mensaje de ayuda que describe +el comando y las opciones disponibles al usarlo. Mira este ejemplo: + +```sh +openfn deploy --help +openfn deploy + +Deploy a project's config to a remote Lightning instance + +Options: + --version Show version number [boolean] + --help Show help [boolean] + -c, --config, --config-path The location of your config file [default: "./.config.json"] + --no-confirm Skip confirmation prompts (e.g. 'Are you sure?') [boolean] + --describe Downloads the project yaml from the specified instance [boolean] + -l, --log Set the log level [string] + --log-json Output all logs as JSON objects [boolean] + -p, --project-path The location of your project.yaml file [string] + -s, --state-path Path to the state file +``` + +## Resolución de problemas {#troubleshooting} + +Esta sección explica cómo resolver algunos errores que podrías encontrar al usar +pull o deploy de OpenFn en tus proyectos. + +### Extraneous Workflow ID {#extraneous-workflow-id} + +#### Descripción {#description} + +Este error ocurre cuando ejecutas `openfn deploy` y los ID de los workflows de +tu projectSpec no coinciden con los de tu instancia de OpenFn. Cuando esto pasa, +el error se muestra en un objeto de error como este: + +``` +[CLI] ✘ Failed to deploy project openfn-data-buffers-prototype: +{ + "errors": { + "workflows": { + "1-ingest-messages": { + "base": [ + "extraneous parameters: workflow_id" + ] + }, + "2-calculate-indicators": { + "base": [ + "extraneous parameters: workflow_id" + ] + } + } + } +``` + +#### Solución {#solution} + +Ejecuta `openfn pull` para actualizar tu instancia local y mantener los ID +sincronizados, incorpora tus cambios y vuelve a ejecutar `openfn deploy`. + +## Otras versiones {#other-versions} + +- [Especificación de portabilidad v2](portability-versions#v2) +- [Especificación de portabilidad v1](portability-versions#v1) diff --git a/i18n/es/docusaurus-plugin-content-docs/current/deploy/portability-versions.md b/i18n/es/docusaurus-plugin-content-docs/current/deploy/portability-versions.md new file mode 100644 index 000000000000..4be40ab1c7b0 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/deploy/portability-versions.md @@ -0,0 +1,116 @@ +--- +title: Versiones de la propuesta de portabilidad +translation_source_hash: 6f60d19ea47e10d8a98feb1db3e323c0fe2297d1 +translation_review_status: machine +--- + +Nuestro compromiso con la portabilidad no ha cambiado a lo largo de la historia +de OpenFn, pero la forma de abordarlo y de ponerlo en práctica ha tomado muchas +formas. + +Este documento sirve de referencia para las versiones anteriores de la +especificación. + +## v3 {#v3} + +El estándar v3 se creó para la plataforma v2 y está vinculado al proyecto +Lightning. + +La v3 usa los comandos y protocolos de despliegue antiguos de la CLI. La app y +la CLI todavía la admiten por completo, pero se está retirando desde mayo +de 2026. + +[Consulta la especificación v3 aquí](/documentation/deploy/portability-v3) + +## v2 {#v2} + +Se usa para exportar desde la plataforma antigua. + +```yaml +jobs: + job-1: + expression: > + registerPatient({ + patient-id: state.data.id, + dob: state.data.birth + }) + adaptor: '@openfn/language-openmrs' + trigger: trigger-1 + credential: my-secret-credential + recurring-job: + expression: > + fn(state => { + console.log("Hi there!") + return state; + }) + adaptor: '@openfn/language-common' + trigger: every-minute + flow-job: + expression: > + fn(state => { + state.data.number = state.data.number * 3 + return state; + }) + adaptor: '@openfn/language-common' + trigger: after-j1 + catch-job: + expression: > + fn(state => { + state.message = "handled it." + return state; + }) + adaptor: '@openfn/language-common' + trigger: j1-fails + +triggers: + trigger-1: + criteria: '{"number":2}' + every-minute: + cron: '* * * * *' + after-j1: + success: job-1 + j1-fails: + failure: job-1 + +# Note that credential keys get copied, but values must be manually entered +# after the export is completed. +credentials: + my-secret-credential: + username: '******' + password: '******' +``` + +## v1 {#v1} + +Propuesta inicial de portabilidad + +```js +const project = { + async: true, + triggers: { + uniqueTriggerId: { + // trigger properties + }, + otherTrigger: { + // other trigger properties + }, + }, + credentials: { + // for now, credentials will not be synced // + // secret1: { + // username: 'mamadou', + // pass: 'shhh', + }, + staticData: { + // static objects that can be accessed from any job + }, + jobs: { + payHealthWorker: { trigger: 'otherTrigger' }, + syncToSalesforce: { + expression: 'uri://github.com/jobs/expresion.js', + trigger: 'uniqueTriggerId', + credential: 'secret1', + }, + }, +}; +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/deploy/portability.md b/i18n/es/docusaurus-plugin-content-docs/current/deploy/portability.md new file mode 100644 index 000000000000..7c1224180afb --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/deploy/portability.md @@ -0,0 +1,197 @@ +--- +title: Portabilidad +translation_source_hash: 9f8f8f055402d673cb33c47b2f12fe9087308afb +translation_review_status: machine +--- + +La especificación de portabilidad es una idea central de los proyectos de +OpenFn. Es a la vez un estándar técnico y un compromiso continuo. Garantiza que +el código escrito en una aplicación de OpenFn se pueda: + +- Desplegar en otra instancia de OpenFn (algo clave para los servicios en + producción que se ejecutan en el país) +- Ejecutar en una máquina local (una gran noticia para quienes desarrollan + workflows o adaptors) +- Sacar por completo de OpenFn y ejecutar en un entorno de ejecución de + JavaScript genérico + +Este manifiesto es la base de las funcionalidades principales de OpenFn Sync, +CLI Deploy, la fusión de sandboxes y la exportación e importación de proyectos +desde la app. + +:::info Especificaciones de portabilidad anteriores + +Nuestro compromiso con la portabilidad no ha cambiado a lo largo de la historia +de OpenFn, pero la forma de abordarlo y de ponerlo en práctica ha tomado muchas +formas. + +Este documento describe la especificación de portabilidad más reciente, +publicada en mayo de 2026. Para ver las especificaciones anteriores, consulta +[Versiones de portabilidad](/deploy/portability-versions.md) + +::: + +Nada en la especificación _tiene_ que ser exclusivo de OpenFn ni de ninguno de +nuestros productos. Imaginamos un futuro en el que el software creado con +Lightning, el OpenFn Integration Toolkit y herramientas de integración o de +workflows completamente nuevas y distintas puedan adoptar esta especificación. + +Si te interesa contribuir a la especificación, contacta a OpenFn a través del +[foro de la comunidad](https://community.openfn.org), escríbenos o sugiere +cambios enviando una pull request aquí. + +## Proyectos como código {#projects-as-code} + +Un principio fundamental de los proyectos de OpenFn es que se pueden representar +como código, en un sistema de archivos o en una rama de git. + +Esto mejora la experiencia de desarrollo en OpenFn porque: + +1. Permite crear y probar workflows localmente +2. Permite el control de versiones de los proyectos y un registro de auditoría + de sus cambios +3. Permite a los usuarios trasladar proyectos existentes entre distintas + instancias (es decir, despliegues) de Lightning. + +## Especificación de proyecto {#project-spec} + +La unidad de portabilidad, lo que codifica un proyecto y permite compartirlo, +sincronizarlo, desplegarlo y editarlo, se llama especificación de proyecto +(project spec). Es una definición abstracta de un proyecto, un plano que se +puede desplegar en muchos lugares. + +Esta estructura define un conjunto de workflows y, para cada workflow, su +configuración principal y la secuencia de steps que ejecuta. Normalmente la +representamos en YAML, porque es cómodo tanto para las personas como para las +máquinas, pero se puede representar en cualquier formato de texto. + +Con una copia de la especificación de un proyecto, puedes: + +- Importar un proyecto a una instancia de la app de OpenFn +- Ejecutar workflows localmente con la CLI +- Desplegar un proyecto en una instancia de la app de OpenFn +- Fusionar proyectos sandbox localmente + +A medida que se introducen nuevas funcionalidades, se agregan claves a esta +estructura con regularidad. Esperamos y nos aseguramos de que todas las +aplicaciones de la especificación admitan estas claves. + +Puedes exportar la especificación de un proyecto desde la app, en la página +Settings. + +Los workflows también se pueden intercambiar por separado con la misma +especificación. Así, puedes importar un workflow a un proyecto existente, o +ejecutarlo localmente sin clonar todo el proyecto. + +## Ejemplo de especificación {#spec-example} + +Este es un ejemplo de especificación de proyecto en formato YAML: + +```yaml +id: portability-example +name: Portability Example +schema_version: '4.0' +collections: + - my-data-cache +credentials: + - name: my-login + owner: some-user@openfn.org +workflows: + - name: Event-based workflow + steps: + - id: transform-data + name: Transform data + expression: fn(s => s) + adaptor: '@openfn/language-common@latest' + - id: webhook + type: webhook + webhook_reply: before_start + enabled: true + next: + transform-data: + disabled: false + condition: always + id: event-based-workflow + start: webhook + - name: Scheduled workflow + steps: + - id: common + name: Common + expression: fn(s => s) + adaptor: '@openfn/language-common@3.3.1' + - id: cron + type: cron + enabled: true + cron_expression: 00 00 * * 1-5 + cron_cursor_job_id: get-data + next: + get-data: + disabled: false + condition: always + - id: get-data + name: Get data + expression: fn(s => s) + adaptor: '@openfn/language-http@latest' + configuration: editor@openfn.org|local login + next: + throw-error: + disabled: false + condition: on_job_failure + common: + disabled: false + condition: '!state.error' + label: sometimes + never: + disabled: true + condition: on_job_success + - id: never + name: never + expression: fn(s => s) + adaptor: '@openfn/language-http@7.2.10' + - id: throw-error + name: throw error + expression: fn(s => s) + adaptor: '@openfn/language-common@3.3.1' + id: scheduled-workflow + start: cron +``` + +El esquema más reciente de un archivo de especificación de proyecto está +definido en TypeScript en +[portability.d.ts](https://github.com/OpenFn/kit/blob/main/packages/lexicon/portability.d.ts). + +## Sincronizar proyectos {#syncing-projects} + +Para saber más sobre cómo desplegar, ejecutar, descargar y editar un proyecto, +consulta nuestra documentación detallada sobre +[CLI Sync](/build-for-developers/cli-sync.md). + +## Recursos vinculados {#linked-resources} + +Aunque diseñamos los proyectos pensando en la portabilidad, algunas +funcionalidades NO son portables por naturaleza. + +Por ejemplo, las credenciales contienen tokens muy sensibles que, por diseño y +por naturaleza, deberían ser muy difíciles de extraer de la plataforma de +OpenFn. Por eso, las credenciales no son realmente portables. Al exportar un +proyecto, las credenciales sensibles no se deberían incluir en ese documento +exportado en texto plano. + +Del mismo modo, las colecciones son una funcionalidad muy ligada a un despliegue +concreto de una plataforma de OpenFn. La especificación de portabilidad no cubre +los datos de las colecciones (aunque, con los permisos adecuados, se pueden +sincronizar datos entre colecciones). + +Este tipo de recursos no portables no forman parte de un proyecto, pero están +VINCULADOS a un proyecto. + +Normalmente, los recursos se vinculan por nombre. Las credenciales y las +colecciones solo declaran que dependen de algo con un nombre determinado, que se +tiene que resolver en tiempo de ejecución. La CLI tiene herramientas para eso, y +al desplegar en una instancia de destino, puede que haya que configurar antes la +instancia para que tenga recursos que coincidan. + +## Especificaciones de portabilidad anteriores {#legacy-portability-specifications} + +Para ver versiones anteriores de nuestro enfoque, consulta +[Versiones de portabilidad](/deploy/portability-versions.md) diff --git a/i18n/es/docusaurus-plugin-content-docs/current/deploy/requirements.md b/i18n/es/docusaurus-plugin-content-docs/current/deploy/requirements.md new file mode 100644 index 000000000000..58cf30039ef1 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/deploy/requirements.md @@ -0,0 +1,170 @@ +--- +title: Requisitos +translation_source_hash: a9076ecb0a1f8904b390ca7474d5443302a760f0 +translation_review_status: machine +--- + +## Planifica primero {#plan-first} + +¿No sabes por dónde empezar? Vuelve a la página +[Planificación](/deploy/options.md) para pensar cómo quieres escalar tus +proyectos de automatización con OpenFn. + +## Evalúa tu capacidad {#assess-your-capacity} + +:::info Ayuda a tu socio a estimar los costos iniciales y continuos + +Usa estas preguntas para empezar a evaluar tu capacidad y tus recursos técnicos, +para que tu socio de despliegue pueda estimar mejor el costo total de propiedad. + +::: + +1. ¿Cómo se despliegan, supervisan y mantienen actualmente las aplicaciones en + la nube en tu organización o gobierno? Cada entorno de despliegue y cada + institución es única, y OpenFn es flexible: según tus procesos actuales de + DevOps, te recomendaremos distintos mecanismos de despliegue. +2. ¿Qué personal de TI y de DevOps hay disponible para apoyar el despliegue y el + mantenimiento de OpenFn? ¿Ese personal tiene experiencia con Docker y + Kubernetes? ¿Y con bases de datos Postgres? +3. ¿El despliegue requerirá alta disponibilidad? (Es decir, si OpenFn va a + recibir solicitudes en tiempo real desde otras aplicaciones en lugar de + ejecutar jobs basados en cron, se deberían ejecutar al menos dos instancias + de OpenFn a la vez detrás de un balanceador de carga, usando "Erlang + distribuido" para lograr una redundancia de la aplicación sin interrupciones. + Si OpenFn no va a recibir solicitudes y solo va a hacer solicitudes salientes + con un horario cron, donde el momento exacto importa poco, mantener un + sistema sin tiempo de inactividad es algo menos importante). + +## Conocimientos necesarios {#knowledge-requirements} + +| Habilidad | Importancia y motivo | +| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Erlang | La **capa de aplicación web y orquestación** de OpenFn es una aplicación Erlang OTP. | +| JavaScript | Los **workers que procesan los jobs** de OpenFn y los propios workflows de OpenFn se basan en JavaScript. Si sabes cómo funciona Node.js, puedes crear workflows que hagan _cualquier cosa_. | +| Postgres | La **base de datos** predeterminada de OpenFn es PostgreSQL | +| Docker | Publicamos todas las **[imágenes](https://hub.docker.com/repository/docker/openfn/lightning/general) de OpenFn** en Docker Hub. Tanto si quieres simplificar la configuración para desarrolladores como si usas tecnologías de orquestación de contenedores, te será útil entender Docker y la computación en contenedores. | +| Kubernetes | En los despliegues de alta disponibilidad, los servicios de Kubernetes ofrecen **balanceo de carga** y simplifican la **administración de contenedores** en varios hosts. Facilitan que las aplicaciones de una empresa sean más escalables, flexibles, portables y productivas. | + +## Requisitos de las máquinas {#machine-requirements} + +:::tip Si eliges "DIY", empieza por lo simple + +Kubernetes _NO_ es obligatorio, pero se recomienda para los despliegues de alta +disponibilidad. Para una configuración más simple, considera un despliegue con +Docker o en servidores físicos (las aplicaciones Erlang OTP funcionan muy bien +en Linux). + +::: + +El SaaS oficial de OpenFn usa [Kubernetes](https://kubernetes.io/) para los +despliegues administrados en Google Cloud, y lo recomendamos para despliegues +escalables y de alta disponibilidad. Con cargas de trabajo variables, es +importante (por estabilidad y por costos) poder escalar el grupo de nodos y los +pods de la aplicación Erlang OTP por separado del grupo de nodos y los pods de +los workers de JavaScript. + +1. Usar un servicio SQL escalable y mantener _al menos_ dos nodos de la + aplicación en ejecución con las siguientes especificaciones ayudará a evitar + tiempos de inactividad no deseados. + 1. **Solicitudes de GKE (requests):** cpu@ "500m", memory@ "1024Mi" + 2. **Límites de GKE (limits):** memory@ "2560Mi" +2. Para despliegues simples sin Kubernetes ni alta disponibilidad, las máquinas + mínimas recomendadas son: + - **Máquina de la aplicación:** 2 vCPU (más o menos un solo núcleo de un + Intel Xeon E5 de 2.6 GHz) con 3.75 GB de memoria y 15 GB de almacenamiento + para la aplicación + 1. Cualquier sistema operativo basado en Linux que pueda ejecutar Docker + (Ubuntu 20.04+ o Debian 9+). + 2. Docker (18 o superior). + - **Máquina de la base de datos:** 2 vCPU (más o menos un solo núcleo de un + Intel Xeon E5 de 2.6 GHz) con 3.75 GB de memoria. El almacenamiento que + necesita la base de datos depende de cuántos días de datos de mensajes + quieras guardar en la propia aplicación (si quieres guardar alguno), y no + se puede determinar sin estimar el volumen de mensajes y runs. Si ampliar + el almacenamiento físico no es difícil en tu despliegue, empieza con 40 GB. + 1. Una instancia de Postgres (como mínimo v14.2), ejecutada en un _servidor + distinto_ del de la aplicación para lograr más estabilidad. +3. Si la aplicación y la base de datos están alojadas en la misma máquina (lo + que no se recomienda), esa máquina debería tener aproximadamente la suma de + los requisitos anteriores. +4. **Ten en cuenta** que, de forma predeterminada, la aplicación ofrece un + endpoint HTTP (sin TLS/SSL). Se espera que un proxy inverso o balanceador de + carga proporcione HTTPS (compatible con HTTP2) y balanceo de carga entre las + instancias. + - _Es decir, el servidor de la aplicación no cifra el acceso web, así que + hace falta un servidor web delante de la aplicación; Nginx con certificados + TLS es un buen punto de partida._ +5. Aunque la arquitectura de red depende del cliente, **recomendamos firmemente + una subred privada** para los servidores de la aplicación. +6. No hace falta desplegar la aplicación de OpenFn en la misma máquina que otros + servicios. Sin embargo, si los sistemas de origen y destino están alojados en + otros servidores, habrá que configurar el enrutamiento de red y las reglas + del firewall para que la integración pueda acceder a ellos. +7. Para la **resolución de problemas y el soporte externo**, los administradores + necesitarán acceso SSH a una cuenta sin restricciones (`sudo` en Ubuntu) si + se requieren servicios de mantenimiento del despliegue. + +## Configuraciones posibles {#possible-configurations} + +Aunque deberías planificar con cuidado tu estrategia de despliegue junto con un +especialista en DevOps, estas configuraciones de ejemplo pueden servirte como +punto de partida. + +### (a) Simple {#a-simple} + +Despliega la aplicación y la base de datos en la misma máquina. + +```mermaid +flowchart TB + subgraph "Linux VM with Docker" + ex1-.-db1 + direction TB + ex1(Erlang OTP App with JS Worker) + db1[(PostgreSQL)] + end +``` + +### (b) Mínima recomendada {#b-recommended-minimum} + +Despliega la aplicación y la base de datos en máquinas distintas. + +```mermaid +flowchart TB + ex1-.-db1 + subgraph "Linux VM with Docker" + direction LR + ex1(Erlang OTP App)-.-js1(Node.js Worker App) + end + subgraph "Linux VM" + db1[(PostgreSQL)] + end +``` + +### (c) Ideal {#c-ideal} + +Escala automáticamente distintos grupos de nodos optimizados en un clúster de +Kubernetes para la aplicación de orquestación en Erlang y la aplicación de +workers de JavaScript. + +Considera usar Postgres como servicio con alta disponibilidad, o ejecutarlo +también en un clúster. + +```mermaid +flowchart TB + ex1-.-db1 + ex1-.-js1 + lb1-->ex1 + subgraph "Load Balancer" + lb1(Ingress) + end + subgraph "VMs/Node Pools for Erlang apps" + direction LR + ex1(Erlang OTP Apps) + end + subgraph "VMs/Node Pool for JS Worker Apps" + js1(Node.js Worker Apps) + end + subgraph "VMs/Node Pool" + db1[(PostgreSQL)] + end +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/design/api-discovery.md b/i18n/es/docusaurus-plugin-content-docs/current/design/api-discovery.md new file mode 100644 index 000000000000..c5a5ff4854e0 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/design/api-discovery.md @@ -0,0 +1,158 @@ +--- +sidebar_label: Descubrimiento de APIs +title: Descubrimiento de APIs para diseñar workflows +translation_source_hash: a378a6fb2934089eafffc5b52421baaedcd7dde2 +translation_review_status: machine +--- + +# Descubre APIs para orientar el diseño de tu automatización de workflows {#discovering-apis-to-inform-your-workflow-automation-design} + +Este artículo explica cómo analizar la documentación de una API y hacer un +borrador del diagrama técnico del workflow. + +## ¿Qué es una API? {#what-is-an-api} + +Las APIs les dicen a las aplicaciones cómo comunicarse. Una API es el +"mensajero" que: + +1. Te dice cómo armar una solicitud, +2. Entrega tu solicitud al proveedor al que se la haces y, después, +3. Te devuelve la respuesta + +| ![Workflow](/img/api_diagram.webp) | +| :-----------------------------------------------------------------: | +| _[Fuente](https://snipcart.com/blog/integrating-apis-introduction)_ | + +OpenFn se conecta con las APIs mediante solicitudes HTTP enviadas por la web. +OpenFn puede automatizar cualquier tarea que permitan las APIs de las +aplicaciones con las que se integra (por ejemplo, si la API de una aplicación +permite enviar pagos, OpenFn puede automatizar el envío de pagos). + +## Cómo analizar la documentación de una API {#how-to-analyze-api-documentation} + +Al principio del proceso de diseño, deberías explorar la documentación de la API +del sistema de destino para ver las opciones de integración. + +### Define las opciones de integración {#determine-integration-options} + +Considera estas preguntas para definir tus opciones de integración, aunque no +haya una API disponible: + +1. ¿Hay una API RESTful? + - Si la hay, ¡OpenFn puede conectarse sin configuración adicional! La API + REST es el estándar de referencia de la mayoría de las aplicaciones web + modernas y suele admitir el formato de datos JSON. +2. ¿Hay un webhook? + - La mayoría de las aplicaciones móviles de recolección de datos ofrecen esta + función. Algunas la llaman "data forwarding", "web callback" o "HTTP push + API". + - Los webhooks envían mensajes o notificaciones automáticamente cuando pasa + algo (por ejemplo, cuando se envía un formulario nuevo, avisan a servicios + externos como OpenFn). Estas notificaciones basadas en eventos permiten la + integración de datos en tiempo real o acciones automatizadas. +3. Si no, ¿qué otras opciones hay para importar y exportar datos de las + aplicaciones de destino? + - ¿Puedes conectarte directamente a una base de datos? + - ¿Hay una forma de importar y exportar archivos? (JSON, CSV, XLS o XML) + - ¿Hay una API heredada (por ejemplo, SOAP) con la que podamos comunicarnos + mediante solicitudes HTTP? + +:::tip + +OpenFn puede conectar cualquier aplicación, aunque no tenga API. Consulta la +sección ["Adaptors"](/adaptors) para saber más. + +::: + +### Autenticación {#authentication} + +La documentación de una API suele tener una sección dedicada a las opciones de +autenticación. Búscala para ver qué métodos de autenticación admite y si hará +falta configurar algo para crear un usuario o una credencial de API nueva. + +Ten en cuenta que los métodos de autenticación con claves de API u OAuth suelen +ser más seguros que la autenticación básica (usuario y contraseña). + +:::tip + +Pide cuanto antes una credencial de API al administrador del sistema de la +aplicación con la que quieres integrarte. Así podrás probar la autenticación en +un entorno de desarrollo o de pruebas y comprobar que puedes conectarte. + +::: + +### Endpoints de la API {#api-endpoints} + +Analiza la documentación para ver qué recursos o entidades y qué funciones +admite la API. Por ejemplo, si quieres registrar pacientes mediante la API, +busca referencias al endpoint "/patients" (o como se llame este recurso en tu +aplicación de destino). + +Esta sección de la documentación incluye un resumen de los métodos de solicitud +HTTP (es decir, POST, GET, etc.) y los parámetros de solicitud que se admiten, +además de ejemplos de solicitudes HTTP que puedes enviar a la API. + +**Los métodos de solicitud HTTP te indican qué operaciones admite la API.** + +1. **C**reate (crear) → POST +2. **R**ead (leer) → GET +3. **U**pdate (actualizar) → PUT o PATCH +4. **D**elete (eliminar) → DELETE + +Por ejemplo, si quieres consultar registros de pacientes de una aplicación, +fíjate si la documentación de la API incluye `GET /patients`. + +### Límites {#limits} + +Presta atención a los límites de la API. La documentación suele tener una +sección dedicada que describe si hay límites o consideraciones sobre las +solicitudes y su frecuencia, la concurrencia y la cantidad de registros. Conocer +estos límites desde el principio te ayuda a diseñar una integración que dé una +automatización escalable y de alto rendimiento. + +## Diagrama técnico del workflow {#technical-workflow-diagramming} + +El resultado del descubrimiento de APIs debería ser un diagrama "técnico" del +workflow. A diferencia del diagrama funcional que se hace durante el +["Descubrimiento"](/design/discovery.md), este refleja las especificaciones +técnicas para integrarse con las aplicaciones de destino. Esas especificaciones +incluyen los métodos u operaciones concretos (por ejemplo, GET o POST) y los +nombres de los recursos de destino en la base de datos o la API (es decir, los +endpoints concretos de la API o las tablas concretas de la base de datos). + +![Workflow](/img/api_example.webp) + +**Al hacer el borrador de tus especificaciones técnicas, ten en cuenta lo +siguiente:** + +1. **Prepárate para los fallos. Tus workflows van a fallar. Piensa qué pasa + cuando fallen…** + - ¿Hay que avisar a alguien? + - ¿Cómo se puede volver a procesar el workflow de forma segura? + - ¿Cómo te aseguras de que no se creen datos duplicados? +2. **Cuando sea posible, usa identificadores únicos para crear una + automatización idempotente. Busca registros existentes en el sistema de + destino con algún identificador único disponible:** + - UUID de registros del sistema (por ejemplo, record_id: asjd2910-b8zy1s0a), + - Códigos únicos (por ejemplo, HOUSEHOLD-10013) y + - Combinaciones únicas de atributos (por ejemplo, familyName + phoneNumber + + village + districtCode) +3. **Si el sistema de destino no tiene una operación "upsert" nativa ni + comprueba duplicados antes de insertar, implementa un patrón upsert ("update + or insert", actualizar o insertar) para…** + - Comprobar si un registro existe con un identificador único… + - Si existe, actualizar el registro. + - Si no, insertar un registro nuevo. +4. **No olvides tener en cuenta los volúmenes de datos. Según tengas que manejar + 1, 10 000 o más de un millón de registros, puede que tengas que cambiar el + enfoque del workflow.** + - Estima el tamaño de los datos que vas a extraer + - Ten en cuenta los límites de la API (registros por página, límites de + frecuencia de solicitudes) + - Considera las operaciones masivas y las solicitudes por lotes + +Mira abajo el diagrama técnico del workflow para sincronizar envíos de +formularios de KoboToolbox con DHIS2. El diagrama funcional original está +[aquí](/design/discovery.md#workflow-requirements-gathering). + +![Workflow](/img/technical_example.webp) diff --git a/i18n/es/docusaurus-plugin-content-docs/current/design/design-overview.md b/i18n/es/docusaurus-plugin-content-docs/current/design/design-overview.md new file mode 100644 index 000000000000..dbc3a062c809 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/design/design-overview.md @@ -0,0 +1,66 @@ +--- +sidebar_label: Resumen del proceso de diseño +title: Resumen del proceso de diseño +translation_source_hash: 089b874cb2e0c86bf464a47396fff6ee07e7cb54 +translation_review_status: machine +--- + +Este artículo describe a grandes rasgos los pasos para diseñar workflows +automatizados, a partir del proceso de implementación estándar del equipo +principal de OpenFn. + +Por lo general, el diseño se hace fuera de OpenFn, en conversación y +colaboración con las personas involucradas del lado del negocio o del programa y +del lado técnico. Una vez cerrado el diseño, la configuración, las pruebas, el +monitoreo y la gestión del workflow se hacen en OpenFn. + +## Términos clave {#key-terms} + +Antes de empezar, asegúrate de entender bien estos términos clave, que usaremos +en toda esta documentación: + +### Workflow (flujo de trabajo) {#workflow} + +El conjunto de instrucciones que determinan cómo resolver un problema o realizar +una tarea. A menudo se divide en tareas más pequeñas e independientes. + +![Workflow](/img/workflow.webp) + +### Automatización de workflows {#workflow-automation} + +El uso de software para realizar estas tareas de forma autónoma, de acuerdo con +reglas de negocio predefinidas y sin necesidad de intervención humana. + +![Automatización de workflows](/img/workflow_automation.webp) + +### Integración de datos {#data-integration} + +El proceso de combinar datos de distintas fuentes en una vista centralizada. La +integración de datos es una forma de lograr la automatización de workflows. Sus +tareas pueden simplificarse, automatizarse y gestionarse con una herramienta de +automatización de workflows. + +![Integración de datos](/img/data_integration.webp) + +## Introducción {#introduction} + +El diseño de la automatización de workflows tiene 5 pasos principales, que se +explican en detalle en otros artículos: + +1. [Descubrimiento y alcance](/design/discovery.md) +2. [Diseño del workflow](/design/design-workflow.md) +3. [Descubrimiento de APIs y diseño técnico](/design/api-discovery.md) +4. [Especificaciones de mapeo de elementos de datos](/design/mapping-specs.md) +5. [Especificaciones del workflow](/design/workflow-specs.md) + +### Caso de uso de ejemplo {#example-use-case} + +En toda la documentación de diseño usaremos como referencia este escenario +ficticio de recolección de datos y automatización de workflows: + +_PatientCare es una ONG de salud con una red de trabajadores comunitarios de +salud que atienden a pacientes en zonas remotas de Guinea. Los trabajadores de +PatientCare recolectan datos de pacientes en +[KoboToolbox](https://www.kobotoolbox.org/). El gobierno de Guinea usa +[DHIS2](http://dhis2.org) como su sistema nacional de información de salud (HIS) +y exige que PatientCare registre todos los datos de pacientes en el HIS._ diff --git a/i18n/es/docusaurus-plugin-content-docs/current/design/design-workflow.md b/i18n/es/docusaurus-plugin-content-docs/current/design/design-workflow.md new file mode 100644 index 000000000000..eac6600fcc44 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/design/design-workflow.md @@ -0,0 +1,75 @@ +--- +sidebar_label: Diseño del workflow +title: Diseña tu primer workflow para automatizar +translation_source_hash: cac640729c28a69e35500d79306c90cf3bcec87a +translation_review_status: machine +--- + +# Diseña tu primer workflow de OpenFn {#designing-your-first-openfn-workflow} + +Este artículo explica cómo usar la información reunida durante el descubrimiento +para definir los pasos concretos del workflow, diseñarlo y hacer un borrador de +diagrama que documente los pasos del proceso que quieres automatizar. + +## ¿Por qué hacer un diagrama del workflow? {#why-diagram-your-workflow} + +Al recopilar los requisitos, puedes esbozar el nuevo workflow con una lista de +pasos o partir de la documentación de un proceso o protocolo de negocio que ya +exista. **Por ejemplo:** + +1. Un paciente nuevo visita la clínica +2. Un trabajador registra al paciente en la aplicación móvil (KoboToolbox) +3. Todos los días, se sincronizan los pacientes nuevos con el sistema nacional + de información de salud (DHIS2) + +Después, considera representar visualmente la estructura y el flujo del workflow +para que las distintas personas involucradas lo entiendan con más facilidad. Un +diagrama ayuda a reflejar: + +1. El flujo o la secuencia correcta de pasos, +2. Las dependencias, +3. Las redundancias y +4. Quién es responsable de cada paso + +## Pasos principales para hacer el diagrama de un workflow {#main-steps-to-workflow-diagramming} + +1. Haz el diagrama de los pasos humanos o manuales del proceso de este workflow, +2. Identifica oportunidades de automatización, +3. Detalla los pasos funcionales del proceso de automatización ideal, +4. Comparte el diagrama con todas las personas involucradas para la aprobación + final y actualízalo cuando haga falta + +El resultado de este ejercicio es una documentación clara de cómo se ejecutará +un proceso de negocio: con automatización, con personas o, a menudo, con una +combinación de ambas. + +## Usa estándares globales en tus diagramas {#diagram-using-global-standards} + +Al hacer diagramas, considera usar estándares globales como BPMN (modelo y +notación de procesos de negocio), para que sean coherentes y los entiendan +personas de fuera. BPMN (más información sobre el estándar +[BPMN 2.0](https://www.omg.org/spec/BPMN/2.0/)) tiene símbolos parecidos a los +de un diagrama de flujo y una notación precisa que se puede traducir a +componentes de procesos de software. + +Estos recursos te ayudarán a aprender y a crear tus propios diagramas BPMN: + +- `BPMN.io`, modelador de código abierto: https://bpmn.io/ +- `Camunda BPMN Tool` incluye una herramienta gratuita y un tutorial: + https://camunda.com/bpmn/ +- `LucidChart` ofrece una interfaz para hacer diagramas muy fácil de usar: + https://www.lucidchart.com/pages/bpmn + +¿Buscas un curso rápido? Este video da un resumen rápido de BPMN y de cómo +usarlo: https://www.youtube.com/watch?v=BwkNceoybvA + +### Ejemplos de diagramas BPMN de OpenFn {#openfn-examples-of-bpmn-diagrams} + +Mira el diagrama BPMN de ejemplo de abajo para esta historia de usuario: + +> Como gerente de programa, quiero extraer los datos de los beneficiarios +> ("tracked entity instances") del sistema DHIS2 de mi país para inscribirlos +> como contactos en mi campaña de SMS configurada en RapidPro y enviarles +> alertas automáticas y novedades del programa. + + diff --git a/i18n/es/docusaurus-plugin-content-docs/current/design/discovery.md b/i18n/es/docusaurus-plugin-content-docs/current/design/discovery.md new file mode 100644 index 000000000000..d250260c3cc9 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/design/discovery.md @@ -0,0 +1,173 @@ +--- +sidebar_label: Descubrimiento y alcance +title: Descubrimiento y alcance de proyectos de OpenFn +translation_source_hash: c971b06c2192e638cce500ea16236908d8832c45 +translation_review_status: machine +--- + +# Descubrimiento y alcance de proyectos de OpenFn {#discovery--scoping-for-openfn-projects} + +Este artículo reúne las preguntas clave de descubrimiento y alcance para +confirmar el valor de negocio, los requisitos centrales del workflow, la +viabilidad técnica y la capacidad del cliente al empezar una implementación +nueva. Se basa en el caso de uso de ejemplo presentado en la +[introducción de la serie](/design/design-overview.md#example-use-case). + +:::tip + +Para exportar o compartir rápidamente estas preguntas, consulta esta +[presentación](https://docs.google.com/presentation/d/1WIc_uNAqapILF7redhTnZXpPRo1jFPSmAjoAplGt42w/edit?usp=sharing). + +::: + +## Preguntas clave {#key-questions} + +### Evaluación del valor de negocio {#business-value-assessment} + +El primer paso del descubrimiento es evaluar el valor de negocio: el posible +retorno de la inversión, las mejoras de eficiencia y otros resultados valiosos +que servirán para medir el éxito. + +**Preguntas que hacer:** + +1. ¿Qué workflows quieres automatizar? +2. ¿Cómo se gestionan actualmente esos workflows? + - ¿Hay un proceso de negocio manual o semiautomático? + - Si lo hay, ¿cuánto tiempo del personal se dedica a gestionarlos? +3. ¿Qué problemas resolverá la automatización? ¿Qué eficiencias o beneficios se + obtendrán? ¿Cuál es el costo de no hacer nada? + - Si no automatizamos estos workflows, ¿cómo seguirán las cosas? + - ¿El workflow actual es lento o inseguro, o empeora la calidad de los datos + o la prestación de servicios? + +**Ejemplo:** + +1. Quiero automatizar la sincronización de datos de casos de KoboToolbox con + DHIS2 Tracker. +2. Actualmente nuestro equipo dedica 3 horas a la semana a exportar a mano los + datos de Kobo e ingresarlos en DHIS2. +3. Esta automatización eliminará el riesgo de error humano al ingresar los datos + a mano, nos ahorrará dinero y tiempo, y nos permitirá atender a más + pacientes. + +## Recopilación de requisitos del workflow {#workflow-requirements-gathering} + +Usa las preguntas de abajo para definir los pasos concretos del workflow. El +resultado debería ser un borrador del workflow o un diagrama del proceso de +negocio (considera hacerlo en [BPMN](https://www.bpmn.org/) para usar una +notación estándar). + +**Preguntas que hacer:** + +1. ¿Qué dispara el workflow y con qué frecuencia debería ejecutarse? (por + ejemplo, en tiempo real o programado) + - ¿Hay una acción de un usuario o un evento del sistema que debería disparar + el workflow? (por ejemplo, en tiempo real al enviar un formulario, o cuando + el estado de un registro cambia a "closed") + - ¿O debería programarse para un día y una hora concretos? (por ejemplo, + todos los días a las 12:00) +2. ¿El workflow necesita un flujo de datos en un solo sentido o en ambos? + - Por ejemplo, si el workflow envía un registro del sistema A al sistema B, + ¿los datos solo tienen que ir en un sentido? ¿O, una vez sincronizados en + el sistema B, hay que devolver algo al sistema A para tener un flujo de + datos bidireccional? +3. ¿Qué volúmenes de datos se esperan? (por ejemplo, 100 derivaciones al mes o + 12 000 formularios al año) + +**Ejemplo:** el workflow debería sincronizar los datos de pacientes de +KoboToolbox con DHIS2 cada vez que se envía un formulario (es decir, +sincronización en tiempo real). Se registran como máximo 5000 pacientes al mes +en Kobo. + +![Workflow](/img/functional_example.webp) + +### Evaluación de viabilidad técnica {#technical-feasibility-assessment} + +Las respuestas a las preguntas de abajo te ayudarán a hacer un borrador del +diagrama de la solución, que documenta exactamente qué instancias se conectarán +y qué interfaces de integración se usarán. + +1. ¿Cuántas instancias hay de los sistemas de destino? (es decir, ¿te conectas a + 1 o a 2 instancias de base de datos?) +2. ¿Los sistemas de destino ya están construidos? ¿Se espera que cambie alguna + configuración? + - Si la configuración todavía está en curso, considera retomar este proyecto + cuando los sistemas estén estables. +3. ¿Hay una API REST disponible? + - Si la hay, comparte la documentación. + - Si todavía no la hay, considera retomar este proyecto cuando la API esté + construida y probada. + - Si no la hay, ¿qué otros métodos hay para importar y exportar datos? + - ¿Es posible conseguir una conexión directa a la base de datos? + - ¿O hay un webhook u otro método para reenviar datos a un sistema externo? + - ¿O una forma de exportar e importar datos con archivos? ¿Qué formatos de + datos hay disponibles? +4. ¿Dónde están alojados los sistemas de destino? ¿Hay requisitos de seguridad o + consideraciones de autenticación conocidas? (por ejemplo, firewalls, + requisitos de VPN o listas blancas de IP) +5. ¿Hay un entorno de pruebas al que podamos acceder para probar la integración + con la aplicación? (Si no lo hay, ¿hay una demo pública de la aplicación que + corra en la misma versión que usas actualmente, para que podamos probar las + APIs?) + +**Ejemplo:** para esta integración solo hay una instancia de PatientCare y una +de DHIS2, y ya están construidas con APIs REST. Las dos están alojadas en +servidores gestionados por PatientCare que exigen una lista blanca de IP para +acceder. + +![Workflow](/img/technical_example.webp) + +### Evaluación de capacidades {#capacity-assessment} + +Las respuestas a las preguntas de abajo te ayudarán a definir los roles del +proyecto para diseñar y entregar la implementación, y a planificar la +capacitación, el despliegue, la administración continua y el soporte. + +1. ¿Cada sistema de destino tiene un administrador de sistemas a tiempo + completo? + - ¿Los administradores pueden apoyar la configuración y las pruebas de la + integración? + - ¿Los administradores podrán ofrecer un entorno de pruebas o de desarrollo? + - ¿Quién aprenderá a administrar OpenFn? +2. ¿Qué conocimientos técnicos tienen? + - ¿Qué otros recursos hay para dar soporte continuo? + - ¿Alguien en la organización tiene experiencia con JavaScript o JSON? +3. ¿Hay interés en aprender a gestionar la implementación de OpenFn de forma + independiente? +4. ¿Quién en la organización se encargará de la gobernanza continua de la + solución y de supervisar la gestión de cambios? ¿Hay recursos para reunirse + con regularidad y revisar las solicitudes de cambio? + +**Ejemplo:** + +| Nombre | Rol | +| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Ian | Administrador del sistema OpenFn, que se encargará de la gestión y el monitoreo continuos | +| Melody | Administradora de PatientCare, que capacitará en el workflow a los usuarios de su sistema | +| Arnis | Administrador de DHIS2, que capacitará en el workflow a los usuarios de su sistema | +| Ramona | Punto focal de programas, que defenderá los intereses de los usuarios, aportará información para definir los requisitos del workflow y se reunirá con regularidad con los usuarios para recoger comentarios, proponer cambios y revisar las solicitudes de cambio con los administradores de sistemas | + +### Documentar la arquitectura de la solución {#documenting-the-solution-architecture} + +Una vez que hayas reunido los requisitos clave de la solución, considera crear +un diagrama de "arquitectura de la solución" que documente lo siguiente: + +1. Los distintos componentes de la solución +2. Los flujos de datos entre esos componentes (destacando el intercambio de + datos dentro de la organización y con servicios de terceros) +3. Los tipos de datos que se intercambian +4. Los puntos de autenticación y acceso + +Estos diagramas aportan transparencia, ayudan a detectar posibles riesgos de +exposición de datos y documentan el cumplimiento de los requisitos de protección +de datos. Mira los diagramas de arquitectura de ejemplo de abajo. + +**Ejemplo 1:** + +![Workflow](/img/solution_diagram1.webp) + +**Ejemplo 2:** + +| ![Workflow](/img/solution_diagram2.webp) | +| :------------------------------------------------------------------------------------------------------------------------------------------------: | +| _[Fuente](https://lucid.app/lucidchart/1e997197-2d67-4393-8394-a532d83561b2/edit?invitationId=inv_85b809a1-6fbd-4275-abdc-618fbd56e90d&page=0_0#)_ | diff --git a/i18n/es/docusaurus-plugin-content-docs/current/design/mapping-specs.md b/i18n/es/docusaurus-plugin-content-docs/current/design/mapping-specs.md new file mode 100644 index 000000000000..915755b1ef9c --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/design/mapping-specs.md @@ -0,0 +1,181 @@ +--- +sidebar_label: Especificaciones de mapeo +title: Escribir especificaciones de mapeo de elementos de datos +translation_source_hash: 12c62347cedde52a1f09d8219e7a03ed1a9792e2 +translation_review_status: machine +--- + +# Mapear elementos de datos para definir las reglas de integración y automatización de datos {#mapping-data-elements-to-define-data-integration--automation-rules} + +Este artículo recorre el proceso de mapeo de elementos de datos con el que se +definen especificaciones, a nivel de entidad y de campo, de cómo se deben +intercambiar, limpiar o transformar los datos en un workflow de integración de +datos. En pocas palabras, el mapeo de datos es el proceso de conectar un campo +de datos de una fuente con un campo de datos de otra (por ejemplo, "patient" en +el sistema A = "person" en el sistema B). + +Una especificación de mapeo de elementos de datos es un tipo especial de +diccionario de datos que sirve como (1) documentación de cómo traduces el +significado entre sistemas y (2) especificación para los desarrolladores que +construyen la solución de automatización del workflow. + +Para cada paso de automatización de tu workflow, documentarás qué elementos de +datos (o metadatos) se usan y las "reglas" para mapearlos, reasignarlos, +limpiarlos, transformarlos o calcularlos. + +![mapeo](/img/mapping_example.webp) + +**Para hacer un borrador de especificación de mapeo de elementos de datos, +tendrás que…** + +1. Exportar los metadatos o pedir una lista de elementos de datos de los + sistemas de destino, +2. Conseguir un registro de "entrada" de muestra del sistema de origen y un + registro de salida de muestra del sistema de destino. En el mejor de los + casos, es un payload JSON de ejemplo o un enlace a registros de ejemplo. En + el peor, es una captura de pantalla o un archivo CSV con datos "ficticios". +3. ¡Empezar a "mapear" los elementos de datos y a registrar las reglas de + transformación! + +| ![mapeo](/img/mapping_process.webp) | +| :---------------------------------------------------------------------: | +| _El proceso de mapeo de datos para soluciones de integración de datos._ | + +## Plantilla de especificación de mapeo de OpenFn {#openfn-mapping-specification-template} + +Puedes documentar elementos de datos, mapeos y reglas con la plantilla de +especificación de mapeo de OpenFn. El equipo de OpenFn creó esta +[plantilla](https://docs.google.com/spreadsheets/d/19sPRLP4zeFgFbtOL1wKh-rc7D0KPMu3etmOOG_x5t68/edit#gid=1275153608) +a partir de lo aprendido al implementar soluciones de integración de datos para +ONG y socios gubernamentales de todo el mundo. Se usa en todos los proyectos de +OpenFn y la mantiene el equipo de OpenFn. + +## Consideraciones sobre el mapeo {#mapping-considerations} + +### Mantener las especificaciones de mapeo {#maintaining-mapping-specifications} + +Cuando tu proyecto de OpenFn esté en producción, el documento de +especificaciones de mapeo puede ser la forma en que tus usuarios interactúan con +tu solución, en un lenguaje que entiende el negocio. Si haces cambios, asegúrate +de que la especificación de mapeo siempre coincida con el código de tus jobs. +Considera también versionar tus especificaciones de mapeo para que las personas +involucradas tengan acceso a las implementaciones anteriores de la solución. + +### Mapeo funcional y mapeo técnico {#functional-vs-technical-mapping} + +Una vez que tu organización (o "el negocio") defina las reglas funcionales de +mapeo de elementos de datos entre los sistemas de origen y de destino, tendrás +que ver qué otros elementos de datos técnicos hacen falta para que la +integración funcione. Pueden ser campos propios del sistema, IDs o parámetros de +la API que funcionan "por detrás" y que el usuario final quizá no vea, pero que +el sistema de destino necesita para compartir los datos. + +### Variables globales y reglas de mapeo {#global-variables-and-mapping-rules} + +A veces, al mapear listas de valores o conjuntos de opciones (por ejemplo, +listas de diagnósticos, jerarquías geográficas o la lista de servicios que +ofrece la organización en todas partes), esos valores son "globales" y hay que +usarlos una y otra vez en toda la implementación del workflow. + +Por ejemplo, imagina que tu aplicación de origen tiene una lista de IDs de +ubicación codificados (por ejemplo, `01, 02, 03`) que hay que mapear a una lista +global de valores de ubicación o unidades administrativas: + +```js +//source location IDs: destination location values +01: 'Western Cape', +02: 'Eastern Cape', +03: 'Gauteng' +``` + +En tu especificación de mapeo, deberías reunir esta lista de valores globales y +de reglas de mapeo en una hoja `globals` aparte, para usarla como referencia en +toda la especificación. Mira un ejemplo en la +[plantilla de especificación de mapeo](https://docs.google.com/spreadsheets/d/19sPRLP4zeFgFbtOL1wKh-rc7D0KPMu3etmOOG_x5t68/edit). + +Luego, al construir el workflow que implementa esta tabla de mapeo de valores +globales, la expresión de tu job podría parecerse a este fragmento de código. + +```js +//Workflow step 1 +//First we use fn() to transform, map & clean our data +fn(state => { + + //Global mapping rules you want to implement in your workflow + const locationMap = { + //location_id from source app: location value in destination app + 01: 'Western Cape', + 02: 'Eastern Cape', + 03: 'Gauteng' + } + + // Here we build the payload of our http request body... + // We assume the input is an array of records + const payload = state.data.map(record => ({ + location: locationMap[record.location_id], //translate location_id to the mapped value + external_id: record.case_id + })); + + return {...state, payload}; +}); + +//Workflow step 2 +//Then we post the payload built in the prior operation to create a record +post('/api/myEndpoint', { + headers: { + 'Content-Type': 'application/json', + }, + body: (state) => state.payload +}); +``` + +#### Gestionar variables globales y mapeos fuera de OpenFn {#managing-global-variables--mappings-outside-of-openfn} + +La plantilla de mapeo de OpenFn, basada en XLS, es útil para definir los +requisitos de mapeo junto con otras personas involucradas. Pero, una vez +definidas esas especificaciones, podrías considerar guardar las reglas de mapeo +`globals` en una aplicación externa, en lugar de escribirlas directamente en el +código de tu job (como en el ejemplo de arriba). + +En su lugar, podrías guardar estas variables globales y reglas de mapeo en una +tabla de base de datos aparte o en una aplicación como +[Open Concept Lab](https://openconceptlab.org/), que tiene una aplicación web +fácil de usar para registrar diccionarios de datos y reglas de mapeo, y es +compatible con API REST. Así podrías consultar estas reglas de mapeo +dinámicamente con OpenFn, para que tu integración use siempre las +especificaciones más recientes. + +En ese caso, la configuración de tu workflow podría verse como la de abajo: el +segundo step del workflow se dedica a consultar la lista de mapeos globales en +la aplicación donde están guardados, para obtener los valores globales más +recientes cada vez que se ejecuta el workflow. + +![ejemplo de workflow con OCL](/img/workflow-ocl-example.webp) + +:::tip + +Para ver la documentación de una implementación de workflow que usa +[Open Concept Lab](https://openconceptlab.org/) para guardar especificaciones de +mapeo y variables globales, +[consulta este](https://docs.google.com/presentation/d/1NEhgHD3P9luYYsJFMfGee8eR8xA03MpgcZcGPpzTLjE/edit#slide=id.g1ed42eefbd1_0_0) +resumen de Médecins Sans Frontières sobre un workflow de OpenFn de ejemplo que +mapea datos de OpenMRS a DHIS2. + +::: + +### Mapear a entidades individuales o agregadas {#mapping-to-individual-or-aggregate-entities} + +Piensa si tu integración necesita un intercambio 1 a 1 de registros individuales +o si hay que resumir o agregar los registros individuales. Puede que tu workflow +tenga que mapear entidades individuales (es decir, un mapeo 1 a 1). Por ejemplo, +puedes mapear un paciente de KoboToolbox a un paciente en DHIS2. Para esos +casos, deberías usar la +[plantilla de mapeo predeterminada de OpenFn](https://docs.google.com/spreadsheets/d/19sPRLP4zeFgFbtOL1wKh-rc7D0KPMu3etmOOG_x5t68/edit#gid=1275153608). + +En cambio, si tu workflow tiene que mapear entidades individuales a una entidad +agregada o resumida (es decir, un mapeo de muchos a 1), puedes empezar con la +[plantilla de mapeo agregado](https://docs.google.com/spreadsheets/d/1JVcM7FEkCeezHXONRaAaEPFks9lS8xO_q51jql_hUtc/edit) +de OpenFn. Por ejemplo, podrías recolectar registros individuales de pacientes +en KoboToolbox, pero querer enviar a DHIS2 un conteo agregado de pacientes para +reportar los resultados de indicadores clave (por ejemplo, la cantidad de +pacientes menores de 18 años). diff --git a/i18n/es/docusaurus-plugin-content-docs/current/design/workflow-specs.md b/i18n/es/docusaurus-plugin-content-docs/current/design/workflow-specs.md new file mode 100644 index 000000000000..6d367e126d03 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/design/workflow-specs.md @@ -0,0 +1,34 @@ +--- +sidebar_label: Especificaciones del workflow +title: Escribir especificaciones de automatización de workflows +translation_source_hash: 3ec9ce462bdeae5af34808d835d0dd1f05d23397 +translation_review_status: machine +--- + +# Escribir especificaciones para soluciones de automatización de workflows {#writing-specifications-for-workflow-automation-solutions} + +**Los resultados clave del proceso de diseño son:** + +1. [Diagrama funcional del workflow](/design/discovery.md#workflow-requirements-gathering) +2. [Diagrama técnico del workflow](/design/discovery.md#workflow-requirements-gathering) +3. [Diagrama de arquitectura de la solución](/design/discovery.md#documenting-the-solution-architecture) +4. [Especificaciones de mapeo de elementos de datos](/design/mapping-specs.md) + +Con todo esto, tendrás lo necesario para cerrar las especificaciones del +workflow y pasárselas a los desarrolladores para que escriban los jobs. + +Cada "tarea" o "paso" del carril de OpenFn en tu diagrama técnico se puede +implementar como una operación distinta en la configuración del workflow. En el +diagrama de ejemplo de abajo, podrías implementar 1 job con 3 operaciones +encadenadas, o 3 jobs con 1 operación cada uno. + +![workflow](/img/workflow_specs.webp) + +**Las especificaciones del workflow deberían enlazar a todos los artefactos de +diseño y destacar lo siguiente:** + +1. La cantidad de jobs de OpenFn necesarios y la función de cada uno +2. Enlaces a ejemplos de entrada y salida y a la documentación de la API +3. Los identificadores únicos +4. Los volúmenes de datos esperados +5. Los requisitos de autenticación diff --git a/i18n/es/docusaurus-plugin-content-docs/current/get-help/support.md b/i18n/es/docusaurus-plugin-content-docs/current/get-help/support.md new file mode 100644 index 000000000000..600da31eac9f --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/get-help/support.md @@ -0,0 +1,31 @@ +--- +title: Soporte para implementaciones de OpenFn +sidebar_label: Obtener ayuda +translation_source_hash: 431c5780f38a380cc95ed50cc70855db919b12c2 +translation_review_status: machine +--- + +## ¡Pregunta a la comunidad! {#ask-the-community} + +Si necesitas ayuda para empezar, tienes preguntas o quieres darnos comentarios +sobre el producto, visita primero nuestra +**[comunidad](https://community.openfn.org)**. Nuestro equipo principal y otros +implementadores de OpenFn siguen todas las publicaciones para ayudarse entre sí, +compartir ejemplos y difundir novedades del producto. + +## ¿Tienes una pregunta sobre tu proyecto en OpenFn.org? {#have-a-question-about-your-project-on-openfnorg} + +Si usas la plataforma SaaS alojada de OpenFn y tienes una pregunta privada sobre +tu proyecto, tu cuenta o la facturación, contacta a nuestro equipo principal en +[support@openfn.org](mailto:support@openfn.org). + +## ¿Necesitas una mano? {#need-helping-hands} + +El equipo principal de OpenFn y nuestros socios certificados ofrecen soporte +empresarial, servicios de implementación y desarrollo, y capacitación para que +tu equipo arranque con fuerza. Visita nuestro sitio web: + +- Sobre los [servicios y precios de OpenFn](https://www.openfn.org/pricing) +- Sobre nuestros [socios certificados](https://www.openfn.org/partners) + + diff --git a/i18n/es/docusaurus-plugin-content-docs/current/get-started/glossary.md b/i18n/es/docusaurus-plugin-content-docs/current/get-started/glossary.md new file mode 100644 index 000000000000..f6e6bb0a6ae0 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/get-started/glossary.md @@ -0,0 +1,180 @@ +--- +sidebar_label: Glosario +title: Glosario de integración de datos +translation_source_hash: 69f9fa591948fe129c1d21ef35db73bdd3674110 +translation_review_status: machine +--- + +Este glosario reúne algunos de los conceptos y términos fundamentales que se +usan al hablar de integración de datos y de automatización de flujos de trabajo. + +No necesitas saber qué significan estas palabras antes de leer nuestra +documentación para usar OpenFn. Pero algunas de las tareas más importantes del +recorrido con OpenFn dan por hecho que entiendes, al menos a grandes rasgos, +cada uno de estos términos. + +Este glosario no es específico de OpenFn. El resto de la documentación y la +[página de conceptos clave](/documentation/get-started/terminology) te ayudan a +hacerte una idea de las partes de OpenFn, de cómo las llamamos y por qué. Este +glosario, en cambio, es un requisito previo a todo eso, pensado para quienes no +tienen experiencia en este ámbito. + +:::tip ¿Falta algo? + +Si encontraste una palabra, una expresión o un concepto que crees que falta en +esta página, abre un issue en [OpenFn/docs](https://github.com/OpenFn/docs), +sugiere un cambio en +[esta página](https://github.com/OpenFn/docs/blob/main/docs/get-started/glossary.md) +o pregunta en la [comunidad](https://community.openfn.org) + +::: + +## API + +API es la sigla de "application programming interface" (interfaz de programación +de aplicaciones). Es la parte de un software (la aplicación) que decidió +hacerse visible (la interfaz) a usuarios externos a la propia aplicación. +Y lo hace de forma programática, es decir, de una forma que permite a los +desarrolladores de otras aplicaciones o sistemas de datos usarla siempre de la +misma manera. + +## Protocolo de API {#api-protocol} + +No hay una regla fija sobre cómo se desarrolla una API, pero con el tiempo han +surgido estándares que hacen que la mayoría de las aplicaciones usen uno de unos +pocos formatos. Así, a un usuario nuevo le resulta más sencillo interactuar con +la API de la plataforma X. Eso es un protocolo de API. Algunos de los nombres +más conocidos son REST, SOAP, JSON y GraphQL. En lugar de reinventar la rueda, +[aquí tienes una buena introducción a en qué se diferencian los protocolos, sus formatos de datos y por qué todo eso importa.](https://frontend-digest.com/beginners-guide-to-apis-protocols-and-data-formats-f80cf7f30425) + +## Base de datos {#database} + +Casi cualquier colección organizada de datos puede llamarse base de datos. Si +tiene una estructura con la que hacer referencia a todo lo que almacena, y lo +que almacena son datos, entonces es una base de datos. + +## Integración de datos {#data-integration} + +El proceso de combinar datos de distintas fuentes en una vista centralizada. La +integración de datos es una forma de lograr la automatización de flujos de +trabajo. Sus tareas pueden simplificarse, automatizarse y gestionarse con una +herramienta de automatización de flujos de trabajo. + +## Fuente de datos {#data-source} + +Una fuente de datos es una aplicación, una base de datos o una tabla que +proporciona datos a otra plataforma. Nada es siempre una fuente de datos. +Por ejemplo, Google Sheets puede ser una fuente de datos, pero también puede +obtener datos de otras fuentes (cargas individuales de archivos CSV o datos que +los usuarios ingresan a mano). Solo la llamamos fuente cuando está +proporcionando datos a otro lugar. En el tiempo, las fuentes de datos son el +punto de partida de cualquier integración. + +## Sistema de datos {#data-system} + +A veces se confunde la diferencia entre una base de datos, una fuente de datos, +una aplicación y un sistema de datos. Un sistema de datos es un conjunto +más complejo de esas otras cosas, normalmente uno que permite a un usuario +interactuar más fácilmente con todos los datos a los que debería tener acceso. +El sistema de datos suele servir como punto de entrada a la multitud de bases de +datos, aplicaciones, tablas, etc., que de otro modo el usuario tendría que +buscar en 12 lugares distintos. + +## Cifrado {#encryption} + +Hoy en día, la seguridad lo es todo. El cifrado es el proceso de tomar algo que +cualquiera puede leer y hacer que solo puedan leerlo las personas que queremos. +OpenFn garantiza que tus datos estén cifrados en todo momento mientras están en +nuestra plataforma. +[Para saber más sobre los distintos tipos de cifrado, puedes consultar aquí.](https://ssd.eff.org/en/node/36) + +## Sistema de archivos {#file-system} + +Un sistema de archivos es a los archivos lo que un sistema de datos es a los +datos. Organiza tus archivos de forma que te resulte fácil recuperarlos de +manera estandarizada (piensa en el sistema de archivos de la computadora de tu +casa, con sus rutas de archivo). Los sistemas de archivos también existen en +otros contextos, y a veces necesitas acceder a ellos para recuperar un archivo +(un documento de Word, un CSV o un archivo de texto plano, entre otros, según tu +caso de uso). La única diferencia real entre los sistemas de archivos y los +sistemas de datos o las bases de datos es el tipo de información que almacenan: +datos frente a archivos. + +## ETL + +ETL son las siglas en inglés de "extract, transform, and load" (extraer, +transformar y cargar). A menudo se consideran las tres partes que componen una +integración de datos. Primero, extraemos (enviamos o recuperamos datos de una +fuente de datos). Después, transformamos (hacemos los cambios necesarios en los +datos para que el sistema o la aplicación de destino los acepte). Por último, +cargamos (los enviamos al destino). + +## Plataforma de integración {#integration-platform} + +Una plataforma de integración (por ejemplo, OpenFn) es una aplicación (o un +conjunto de aplicaciones) que ayuda a las organizaciones a configurar, ejecutar +y mantener o gestionar las integraciones entre todos sus sistemas. + +### iPaaS + +Puede que también veas la sigla "iPaaS". Significa "integration platform as a +service" (plataforma de integración como servicio) y es un tipo de "software +como servicio" (o "SaaS"). El SaaS es un modelo de compra de software en el que +el software se paga solo a medida que se usa (a menudo mes a mes), en lugar de +comprarse por adelantado o regalarse. + +## Metadatos {#metadata} + +Son datos que nos dicen algo sobre nuestros datos. En una tabla, por ejemplo, +son los nombres de las columnas, el número de filas, etc. Los metadatos suelen +salir en las conversaciones sobre privacidad. Por ejemplo, los reguladores +pueden querer asegurarse de que _solo los metadatos_ pasen del Ministerio A al +Ministerio B, y no la información de identificación personal (PII) de las +propias personas. + +## Push, pull y streaming {#push-pull-and-streaming} + +El push (envío) se produce cuando una acción en la fuente de datos hace +que esta envíe datos al destino. El pull (recuperación) es lo contrario: +el sistema de destino le pide los datos a la fuente a partir de alguna acción, +en lugar de esperar a que la fuente los envíe por su cuenta. El streaming +es algo distinto: se da cuando una fuente de datos envía datos a un sistema de +destino de forma prácticamente constante. + +## Webhook + +Un [webhook](/documentation/build/triggers#webhook-event-triggers) (también +llamado web callback o API HTTP push, ¡gracias, +[SendGrid](https://sendgrid.com/blog/whats-webhook/)!) es una función de una +aplicación que permite hacer push. Suele configurarse para avisar a una +URL externa cuando ocurre un evento. Un administrador de sistemas podría crear +un "webhook" que avise a una plataforma de integración cada vez que ocurra algún +evento, para que la iPaaS empiece a ejecutar un workflow complejo. + +## Datos estructurados y no estructurados {#structured-and-unstructured-data} + +Los datos estructurados son datos que tienen metadatos. Los datos no +estructurados tienen muy pocos metadatos (aunque probablemente conserven cosas +como la fecha de creación o de actualización). Sin metadatos sobre su formato, +es más difícil trabajar con los datos no estructurados mediante programación. +Para hacer bien un ETL sobre datos no estructurados necesitamos otro tipo de +reglas. Los datos estructurados son un punto de partida más fácil, porque +sabemos qué esperar de una columna con nombre, tipo de dato, tamaño de campo, +etc. + +## Flujo de trabajo {#workflow} + +El conjunto de instrucciones que determinan cómo resolver un problema o realizar +una tarea. A menudo se divide en tareas más pequeñas e independientes. + +## Automatización de flujos de trabajo {#workflow-automation} + +El uso de software para realizar tareas o un proceso de negocio de forma +autónoma, de acuerdo con reglas de negocio predefinidas y sin necesidad de +intervención humana. + +## Writeback + +Se refiere a que un sistema de destino haga un cambio en una fuente de datos. +Cuando tu aplicación de destino recibe información de una fuente de datos y +quiere hacer algo en la fuente como respuesta, eso es writeback. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/get-started/home.md b/i18n/es/docusaurus-plugin-content-docs/current/get-started/home.md new file mode 100644 index 000000000000..cbc24cce4553 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/get-started/home.md @@ -0,0 +1,151 @@ +--- +title: ¿Qué es OpenFn? +id: home +sidebar_label: ¿Qué es OpenFn? +slug: / +translation_source_hash: 97ba6c3bda9542d0d78cd1b33c23496cd233e174 +translation_review_status: machine +--- + +**OpenFn es el principal +[bien público digital](https://digitalpublicgoods.net/digital-public-goods/) +para la automatización de flujos de trabajo**. + +Es una plataforma que más de 70 ONG y ministerios de gobierno han usado para +automatizar e integrar procesos de negocio y sistemas de información críticos. + +**Conecta cualquier aplicación** con la biblioteca de [adaptors](/adaptors/) (es +decir, conectores) de código abierto de OpenFn. Desde los servicios de última +milla hasta los informes a nivel nacional, OpenFn mejora la eficiencia y la +eficacia, y hace posible una interoperabilidad segura, estable y escalable en +todos los niveles. + +Puedes desplegar OpenFn localmente o usar la segura +[plataforma alojada en la nube](https://openfn.org/pricing). Consulta la +[documentación de despliegue](/documentation/deploy/options) para saber más +sobre las opciones y los requisitos de despliegue. + +Para apoyar a quienes implementan, OpenFn cuenta con una +[comunidad](https://community.openfn.org) en línea, documentación y +[soporte](mailto:support@openfn.org). Escribe a +[partnerships@openfn.org](mailto:partnerships@openfn.org) para conocer a los +socios de implementación de OpenFn y el Programa de Socios de OpenFn. + +:::tip Automatización, integración e interoperabilidad + +OpenFn es software de código abierto que facilita a los gobiernos y las ONG +_conectar_ las distintas tecnologías que usan, automatizar procesos de negocio +críticos y escalar sus intervenciones. OpenFn hace posible la automatización, la +integración y la interoperabilidad de datos para las organizaciones de mayor +impacto del mundo. + +::: + +## Nuestros productos {#our-products} + +OpenFn ofrece un conjunto de productos totalmente interoperables entre sí, así +que nuestros usuarios pueden pasar libremente de uno a otro o usarlos todos. + +Todos los productos de OpenFn, salvo la iPaaS OpenFn v1, forman parte del +`OpenFn Integration Toolkit`, libre y de código abierto, que es un **bien +público digital** (un "DPG", por sus siglas en inglés) reconocido en el +[registro de DPG](https://digitalpublicgoods.net/registry/) y en la +[Global Goods Guidebook](https://digitalsquare.org/resourcesrepository/global-goods-guidebook) +de Digital Square. + +Los productos principales de OpenFn son: + +- **[OpenFn/lightning](https://github.com/OpenFn/lightning)**: nuestra + plataforma de código abierto de integración de datos y automatización de + flujos de trabajo. Es la versión "v2", la que se usa actualmente. +- OpenFn/platform: la primera versión de nuestra plataforma. Reemplazada por la + v2, su retiro está previsto para 2025 +- [**OpenFn/adaptors**](https://github.com/OpenFn/adaptors): código fuente de + los adaptors +- [**OpenFn/kit**](https://github.com/OpenFn/kit): CLI, herramientas para + desarrolladores y entornos de ejecución de Javascript +- [**OpenFn/docs**](https://github.com/OpenFn/docs): documentación y código + fuente de docs.openfn.org + +Consulta todos los productos y el código en +[GitHub.com/OpenFn](https://github.com/OpenFn). + +### OpenFn v2: Lightning ⚡ + +Cuando oigas "OpenFn", piensa en +[OpenFn/lightning](https://github.com/OpenFn/lightning/). La v2 es una +aplicación web de automatización de flujos de trabajo _totalmente de código +abierto_ que puede desplegarse y ejecutarse en cualquier lugar. Está pensada +para gobiernos y ONG que buscan capacidades de vanguardia en automatización de +flujos de trabajo e integración e interoperabilidad de datos, con gestión de +usuarios y auditoría completas, en una plataforma gestionada _o_ totalmente +autoalojada. + +La versión 2 aprovecha la misma tecnología central, probada y confiable, que +OpenFn v1, e incluye una interfaz visual mejorada para crear integraciones. + +![Canvas de un workflow de OpenFn](/img/case_referral_workflow.webp) + +**Mira la lista de reproducción +[OpenFn v2 Basics](https://www.youtube.com/watch?v=U0MXYRXkDnI&list=PL1pD3-abjHJ0L01RjouO2xOWKtEUYi8e4&ab_channel=OpenFn.org)** +en Youtube, con videos que te ayudarán a empezar rápido, o consulta las demás +páginas de la documentación. + +:::info OpenFn v2 reemplaza a v1 + +OpenFn v2 está disponible para cualquier usuario nuevo. Todas las organizaciones +que usan actualmente la plataforma heredada OpenFn v1 se migrarán a OpenFn v2 +antes de finales de 2024. + +::: + +### OpenFn v1 + +OpenFn v1 es la _plataforma de integración como servicio_ o "iPaaS" heredada de +OpenFn, lanzada en 2015. OpenFn v1 era open-core, con una aplicación web +propietaria. + +La plataforma v1 se retirará en 2025 y la reemplazará OpenFn v2, totalmente de +código abierto (ver arriba). + +### Herramientas para desarrolladores de OpenFn {#openfn-developer-tooling} + +[OpenFn/kit](https://github.com/OpenFn/kit) ofrece una CLI y un conjunto de +herramientas para desarrolladores con las que puedes escribir y probar +workflows, gestionar proyectos de OpenFn y desarrollar +[adaptors](https://github.com/openfn/adaptors). + +:::note Explora todo el código de OpenFn + +Puedes consultar la documentación técnica y el código fuente de las herramientas +de integración y los adaptors de OpenFn, totalmente libres y de código abierto +("FOSS"), en sus repositorios en [GitHub.com/OpenFn](https://github.com/openfn), +o consultar la sección [Despliegue](/documentation/deploy/options) para ver un +resumen de las opciones FOSS y más documentación. + +::: + +## Comunidad {#community} + +Para hacer preguntas, reportar problemas o aprender de otras personas que +implementan OpenFn, visita nuestro foro de Discourse en +[community.openfn.org](https://community.openfn.org). Regístrate y únete a la +conversación. Suele ser la forma más rápida de obtener ayuda si tienes preguntas +que no se responden aquí. + +Si tienes preguntas sobre nuestros productos, pregunta en la comunidad o escribe +al equipo principal a [support@openfn.org](mailto:support@openfn.org). + +## ¿Quién lo desarrolla? {#who-is-it-built-by} + +El principal responsable de OpenFn es +[Open Function Group](https://openfn.org/about), un equipo global de +especialistas en automatización de flujos de trabajo e integración de datos, y +colaboradores principales de OpenFn. Conoce más sobre la gobernanza de OpenFn +[aquí](https://github.com/OpenFn/governance). + +El [bien público digital](https://app.digitalpublicgoods.net/a/11038) OpenFn ha +sido creado por y para la creciente comunidad de ONG, gobiernos, socios de +"tecnología para el bien" y colaboradores de código abierto que trabajan en +intervenciones de salud y humanitarias en países de ingresos bajos y medianos +(LMIC, por sus siglas en inglés). diff --git a/i18n/es/docusaurus-plugin-content-docs/current/get-started/implementation-checklist.md b/i18n/es/docusaurus-plugin-content-docs/current/get-started/implementation-checklist.md new file mode 100644 index 000000000000..dccbd0cd03c7 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/get-started/implementation-checklist.md @@ -0,0 +1,127 @@ +--- +sidebar_label: Lista de verificación de implementación +title: Lista de verificación de implementación para planificar tu próximo proyecto de integración +translation_source_hash: 2776efe8363e6ba09f194f27ccf75b6531694f33 +translation_review_status: machine +--- + +# Lista de verificación de implementación {#implementation-checklist} + +Esta +[lista de verificación de implementación](https://docs.google.com/spreadsheets/d/1_XY0nx0OLNUsogrIHnRaSTyZ-KdcSXks-tqwm3ZfMc4/edit#gid=72612093) +se basa en nuestra experiencia en proyectos de interoperabilidad con organismos +gubernamentales de distintos países (como oficinas de país de UNICEF, el +Ministerio de Servicios Sociales de Camboya y el Ministerio de Salud de +Tailandia). Ofrece una guía de implementación y planificación con los hitos +clave de la mayoría de los proyectos de interoperabilidad e integración. + +Aunque conviene adaptar esta lista de verificación a cada implementación, las +tareas que describe forman un plan de trabajo modelo que puede ayudar a +cualquier organización a prepararse para su próxima implementación. **El proceso +de implementación se divide en las siete fases que se resumen a continuación. +Consulta la lista de verificación para ver los pasos en detalle.** + +:::tip + +Mira un ejemplo real: el repositorio de UNICEF Camboya documenta los resultados +de esta lista de verificación en un proyecto de interoperabilidad implementado +para el Ministerio de Asuntos Sociales, Veteranos y Rehabilitación Juvenil de +Camboya y ONG asociadas: +[openfn.github.io/unicef-cambodia/](https://openfn.github.io/unicef-cambodia/) + +::: + +## (1) Preparación de la implementación {#1-preparing-for-the-implementation} + +Sienta las bases del proyecto: crea un plan de proyecto, define los roles y las +responsabilidades, documenta el valor de negocio de la implementación y confirma +su viabilidad técnica. + +Resultados clave: + +- Evaluación del valor de negocio +- Requisitos generales de los workflows +- Evaluación de viabilidad técnica +- Evaluación de capacidades + +## (2) Descubrimiento y diseño: requisitos funcionales de los workflows {#2-discovery--design---functional-workflow-requirements} + +Recopila y documenta las historias de usuario y los requisitos funcionales de +los workflows. + +Resultados clave: + +- Diagrama de arquitectura de la solución +- Diagramas de workflows (funcionales) +- Especificaciones de mapeo de elementos de datos (funcionales) + +## (3) Descubrimiento y diseño: especificaciones técnicas {#3-discovery--design---technical-specifications} + +Itera sobre los requisitos de los workflows para definir las especificaciones +técnicas de cómo se implementará cada workflow. Por ejemplo, define a qué +endpoints de la API concretos hay que acceder y qué métodos u operaciones HTTP +usar en cada uno. + +Resultados clave: + +- Diagrama de arquitectura de la solución +- Diagramas de workflows (técnicos) +- Especificaciones de mapeo de elementos de datos (técnicas) + +## (4) Desarrollo {#4-build} + +Configura el workflow en OpenFn.org y desarrolla y prueba los jobs y adaptors +que se usarán en él. + +Resultados clave: + +- Configuración del proyecto de OpenFn +- Jobs +- Adaptors nuevos o actualizados (si hacen falta) +- Borrador de la "Project Security Configuration Checklist" para documentar los + ajustes de configuración implementados + +## (5) Pruebas {#5-testing} + +Crea un conjunto de pruebas y realiza las pruebas de aceptación de usuario +(UAT). Después, incorpora los comentarios recibidos e itera sobre el proceso de +pruebas. + +Resultados clave: + +- Conjunto de pruebas completo +- Lista de nuevas solicitudes pendientes (si los comentarios identifican + necesidades para fases futuras) +- "Project Security Configuration Checklist" completa + +## (6) Capacitación y preparación para la puesta en marcha {#6-training--prep-for-go-live} + +Capacita a los administradores de OpenFn y a los usuarios finales de los +sistemas de destino, y documenta lo que se implementó. En esta fase también se +migran la configuración y el código a los entornos de producción. + +Resultados clave: + +- Documentación publicada +- Grabación en video de la capacitación +- "Project Security Configuration Checklist" aprobada +- Proyecto de OpenFn listo para usar + +## (7) Despliegue y soporte {#7-rollout--support} + +"Activa" los workflows de OpenFn para la puesta en marcha y establece +estructuras de soporte y un modelo de gobernanza para la gestión del cambio. + +Resultados clave: + +- Proyecto de OpenFn "en producción" +- Modelo de soporte documentado + +## ¿Preguntas o comentarios? {#questions-or-feedback} + +Si tienes aportes, comentarios o preguntas, ¡contribuye! Envía una pull request +a esta página de documentación en GitHub o deja un comentario en la +[OpenFn Community](https://community.openfn.org/). + +¿Te interesa recibir **capacitación sobre el proceso de implementación de +OpenFn**? Contacta a [partnerships@openfn.org](mailto:partnerships@openfn.org). diff --git a/i18n/es/docusaurus-plugin-content-docs/current/get-started/security-compliance.md b/i18n/es/docusaurus-plugin-content-docs/current/get-started/security-compliance.md new file mode 100644 index 000000000000..f851002fb156 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/get-started/security-compliance.md @@ -0,0 +1,140 @@ +--- +sidebar_label: Seguridad y cumplimiento +title: Seguridad y cumplimiento +translation_source_hash: e7615c14f8ea9167b2580775204ed61653b87ce7 +translation_review_status: machine +--- + +# Todo sobre S³ {#all-about-s} + +En OpenFn damos prioridad a soluciones **seguras**, **estables** y +**escalables** (**"S³"**, por sus iniciales en inglés, es nuestro lema), en ese +orden. Protegemos tus datos, mantenemos las integraciones en funcionamiento y +crecemos junto a tu organización. Gobiernos y ONG de todo el mundo confían en +nosotros. + +✓ Configuración segura por defecto en la plataforma para proteger tus datos y +reducir al mínimo las brechas de seguridad + +✓ Ajustes de seguridad sólidos y configurables para garantizar el cumplimiento +de tus políticas + +✓ Crea canalizaciones de datos de "persistencia cero" para controlar por +completo dónde se almacenan los datos + +✓ Capacitación y orientación sobre la implementación de la seguridad para los +equipos de tus proyectos ([más información](/get-started/security.md)) + +Consulta nuestro sitio web principal para saber más sobre +[seguridad y confianza](https://www.openfn.org/trust) y +[cumplimiento](https://www.openfn.org/compliance) en OpenFn. + +## Cumplimiento {#compliance} + +Las implementaciones de OpenFn son muy configurables y pueden desplegarse en +cualquier lugar, lo que ayuda a garantizar el cumplimiento de las políticas de +privacidad y seguridad de datos de tu país o de tu organización. + +**Para saber más sobre cómo entendemos el cumplimiento, sobre todo de normas +como el GDPR o la HIPAA, consulta nuestra página web de +[cumplimiento](https://www.openfn.org/compliance).** Contacta a +[nuestro equipo principal](mailto:support@openfn.org) si te interesa recibir +consultoría y asesoramiento sobre cómo desplegar y configurar tu implementación +de OpenFn para cumplir al 100 % con las normas. + +## OpenFn y el almacenamiento de datos {#openfn-and-data-storage} + +En tu ecosistema digital, **OpenFn suele funcionar como una solución de +procesamiento y transferencia de datos, no como un servicio de almacenamiento de +datos.** + +Como bien público digital de código abierto, OpenFn puede desplegarse en +cualquier lugar ([ver documentación](/deploy/options.md)), y los workflows +pueden configurarse para respetar los acuerdos de intercambio de datos y las +políticas de seguridad de tu organización. + +Consulta las páginas de documentación de +[gestión de proyectos](/manage-projects/platform-mgmt.md) para saber más sobre +los ajustes de proyecto y de +[almacenamiento de datos](/manage-projects/io-data-storage.md). + +El siguiente diagrama muestra una arquitectura de ejemplo en la que incluso +OpenFn Cloud puede configurarse como una **canalización de datos de +"persistencia cero"** para cumplir los requisitos de seguridad y residencia de +datos. Así, los socios pueden configurar y poner a prueba proyectos rápidamente +con la plataforma de OpenFn alojada en la nube, lista para usar, y migrar a un +despliegue local cuando estén preparados para escalar. + +![Arquitectura de ejemplo](/img/zero-persistence.webp) + +Para borrar los datos de tu proyecto en cualquier momento, puedes +[borrar tu proyecto](/manage-projects/platform-mgmt.md) o +[borrar tu cuenta](/manage-users/user-profile.md). + +## Cifrado {#encryption} + +OpenFn Cloud almacena los datos en un producto Cloud SQL orientado a la +seguridad, que garantiza cifrado de 256 bits en reposo, y solo permitimos +conexiones con TLS/SSL. + +Cifrado de la plataforma: + +- Advanced Encryption Standard de 256 bits +- Cifrado SSL/TLS en tránsito +- Credenciales y secretos cifrados en disco + +Más información en [openfn.org/trust](https://www.openfn.org/trust#encryption). + +## Credenciales {#credentials} + +Las [credenciales](/manage-projects/manage-credentials.md), que dan a OpenFn +acceso a las API de tus distintas tecnologías, están cifradas en reposo. Así, en +el improbable caso de una brecha en la base de datos, un atacante no podría leer +tu información de autenticación sin acceder a varios servidores protegidos de +forma independiente. + +Las conexiones con tus aplicaciones de destino solo se hacen por HTTPS, con SSL +y, en la mayoría de los casos, autenticación básica. Las especificaciones +técnicas de la conexión dependen del endpoint REST de la aplicación a la que te +conectas. Encontrarás la documentación técnica de cada adaptor en la +[documentación de adaptors](/adaptors) o en su repositorio en Github, en +[github.com/OpenFn/adaptors](https://github.com/OpenFn/adaptors). + +Solo tú (quien las creó) puedes ver las credenciales, que se cargan en tu +entorno de ejecución privado para ejecutar los jobs. Puedes borrarlas en +cualquier momento y se eliminarán del sistema. +[Consulta la documentación](/manage-users/user-credentials.md) para saber más +sobre la gestión y el uso compartido de credenciales en OpenFn. + +## Gestión del acceso de usuarios y RBAC {#user-access-management-and-rbac} + +OpenFn permite gestionar el acceso de los usuarios mediante **control de acceso +basado en roles (RBAC)**, con el que los administradores asignan permisos +detallados tanto a nivel de entorno como de proyecto. Los roles (por ejemplo, +Admin, Editor y Viewer) controlan quién puede ver, editar, ejecutar o gestionar +workflows y credenciales. El acceso puede limitarse a proyectos o +configuraciones de entorno concretos, con registros de auditoría y tokens de API +de alcance limitado para garantizar la seguridad y el cumplimiento. + +Cuando invitas a nuevos usuarios a trabajar en tu proyecto como colaboradores, +se les asigna un rol que determina sus permisos. Consulta la documentación sobre +[colaboración](/manage-projects/collaboration.md) y +[roles de usuario](/manage-projects/user-roles-permissions.md) para más +información. + +Cuando los usuarios se registran en la plataforma, se les pide que creen una +contraseña segura. Los superadministradores de OpenFn también pueden activar la +[autenticación multifactor](/manage-users/user-profile.md), la caducidad de las +contraseñas y el bloqueo de cuentas inactivas. + +:::info ¿Más preguntas sobre la seguridad de OpenFn? + +Primero, consulta las páginas de [confianza](https://www.openfn.org/trust) y +[cumplimiento](https://www.openfn.org/compliance) de nuestro sitio web, así como +la [guía de implementación segura](/get-started/security.md). + +Haz tus preguntas en la [comunidad](https://community.openfn.org/) o +[contacta a nuestro equipo principal](mailto:security@openfn.org) para consultas +privadas. + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/get-started/security.md b/i18n/es/docusaurus-plugin-content-docs/current/get-started/security.md new file mode 100644 index 000000000000..4142c40e2f13 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/get-started/security.md @@ -0,0 +1,122 @@ +--- +sidebar_label: Seguridad en las implementaciones +title: Consideraciones de seguridad para proyectos de integración de datos +translation_source_hash: 366084aa20c5ce7de23ca63331268d1b40fca41a +translation_review_status: machine +--- + +# Directrices de seguridad para implementaciones de integración de datos {#security-guidelines-for-data-integration-implementations} + +Aunque las tecnologías de tu solución de integración puedan considerarse +seguras, la integración de datos sigue teniendo muchos riesgos de seguridad, +sobre todo durante la implementación. Por eso, con el apoyo de Digital Square, +elaboramos una **Guía de seguridad para implementaciones de integración de +datos**. + +Desde 2014, en Open Function Group (los principales responsables de OpenFn) +hemos ayudado a implementar casi 100 soluciones de integración de datos para más +de 45 ONG y gobiernos de todo el mundo. Gracias a nuestro trabajo con los +equipos de seguridad de distintos socios, a nuestra propia investigación y +desarrollo, a las consultas con expertos en seguridad internos y externos, y a +las alianzas con otras comunidades de práctica, conocemos bien las buenas +prácticas y las consideraciones de seguridad para proyectos de integración de +datos, y queremos compartirlas con toda la comunidad del desarrollo digital. + +**Esta guía busca ayudar a quienes implementan soluciones digitales en las +comunidades de bienes públicos digitales y de bienes globales a entender mejor +los riesgos de seguridad, y presenta 23 buenas prácticas para las distintas +fases de implementación de los proyectos de integración de datos.** También +enlaza a algunos recursos de código abierto de OFG que nuestro equipo usa en su +propio proceso de implementación de proyectos de OpenFn. + +Más abajo en esta página encontrarás la lista completa de las 23 buenas +prácticas. + +**Para acceder a la guía, consulta las diapositivas de abajo o haz clic en el +enlace para compartirla y descargarla:** +[https://bit.ly/security_guidebook](https://bit.ly/security_guidebook) + +

+ +

Integración de datos segura: 23 buenas prácticas de implementación

+

Principios básicos

+
    +
  1. Conoce las políticas pertinentes sobre el intercambio, el almacenamiento y la protección de datos
  2. +
  3. Extrae y transfiere solo los datos imprescindibles
  4. +
  5. Documenta, documenta, documenta
  6. +
+

Analizar y planificar

+
    +
  1. No des por sentada la seguridad de las API
  2. +
  3. Reserva tiempo para las pruebas de seguridad
  4. +
+ +

Diseñar

+
    +
  1. Recurso: plantilla de especificación de mapeo
  2. +
  3. Recurso: diagrama de flujo de datos de la arquitectura
  4. +
  5. Recurso: Project Security Configuration & Go-Live Checklist (lista de verificación de configuración de seguridad y puesta en marcha del proyecto)
  6. +
  7. Ten en cuenta la idempotencia, los identificadores únicos y las operaciones de tipo "upsert" para garantizar la integridad de los datos
  8. +
  9. Diseña pensando en los fallos y en el reprocesamiento de transacciones
  10. +
  11. Ten en cuenta la validación de datos
  12. +
+

Crear

+
    +
  1. Usa el seguimiento de cambios y el control de versiones
  2. +
  3. Cifra siempre que puedas
  4. +
  5. Usa una autenticación sólida; no hables con desconocidos
  6. +
  7. Usa ámbitos de autorización para limitar el acceso
  8. +
  9. Registra las transacciones para supervisar la actividad y controla qué información se registra
  10. +
+

Desplegar

+
    +
  1. Vuelve a probar, sobre todo las credenciales, antes del despliegue
  2. +
  3. Capacita a los usuarios y a los administradores de sistemas en la seguridad de la integración
  4. +
  5. Revisa de nuevo tus requisitos de seguridad antes de la puesta en marcha
  6. +
  7. Define los puntos de contacto para reportar problemas de seguridad
  8. +
+

Supervisión y gestión continuas

+
    +
  1. Considera modelos de gobernanza para la gestión continua y los cambios de requisitos
  2. +
  3. Capacita a los socios en la gestión del cambio
  4. +
  5. Ten una estrategia de gestión del acceso
  6. +
+ +Sigue leyendo para conocer otros recursos y comunidades de implementadores que +te pueden interesar. + +### Recursos citados en la guía {#resources-referenced-in-the-guidebook} + +- [Principles of Digital Development Privacy and Security Guide](https://digitalprinciples.org/wp-content/uploads/PDD_Principle-AddressPrivacySecurity_v2.pdf) +- [UNICEF policy on personal data protection](https://www.unicef.org/supply/media/5356/file/Policy-on-personal-data-protection-July2020.pdf.pdf) +- [International Committee of the Red Cross Handbook on data protection in humanitarian action](https://www.icrc.org/en/data-protection-humanitarian-action-handbook) +- [GDPR Quick Guide](https://gdpr.eu/what-is-gdpr/) +- [Sanity.io A Rough Guide to Running a GDPR Compliant SaaS Business](https://www.sanity.io/blog/a-rough-guide-to-running-a-gdpr-compliant-saas-business) +- [OWASP API Security Project](https://owasp.org/www-project-api-security/) +- [GovStack Security & API Standards](https://www.govstack.global/wp-content/uploads/2021/08/Security_Building_Block_Definition_1.0.1.pdf) +- [Health Data Governance Principles](https://www.healthdataprinciples.org/) +- [CDC Health Data Privacy, Confidentiality, and Security Guidelines](https://gicsandbox.org/sandbox-cms/health-data-privacy-confidentiality-and-security-guidelines-development-toolkit#dd01fcf80d4d46f08a099b282bc23f16) + +### Recursos de OpenFn {#openfn-resources} + +Encontrarás más orientación sobre implementación en todo este sitio de +documentación. Si usas OpenFn, puedes saber más sobre su seguridad y +cumplimiento normativo en [openfn.org/trust](http://openfn.org/trust) y +[openfn.org/compliance](http://openfn.org/compliance). + +Estas son las principales plantillas y recursos de OpenFn citados en la guía: + +- [Plantilla de especificación de mapeo](https://docs.google.com/spreadsheets/d/1IqTIgOzyOztEevXbgY_4uE8Y8tiHXufZXx-IyJZase0/edit#gid=1822444315) +- [Diagrama de arquitectura de la solución](https://lucid.app/lucidchart/1e997197-2d67-4393-8394-a532d83561b2/edit#?templateid=fb96ae05-e288-4d1f-b3fc-2cbf7641a7cc) +- [Recursos de diagramas BPMN](/documentation/design/design-workflow#diagram-using-global-standards) +- [Project Security Configuration & Go-Live Checklist](https://docs.google.com/document/d/1CbQkN7SqNmXeqt3nMTYP4ioQlTuwF2LbDkkFqhp0zsU/edit?usp=sharing) + +### Comunidades de práctica y otros expertos {#communities-of-practice--other-experts} + +Estas son otras comunidades que puedes seguir para obtener más orientación sobre +seguridad. + +1. [OpenHIE Privacy & Security Working Group](https://wiki.ohie.org/display/resources/Privacy+and+Security+Working+Group+Call) +2. [GovStack](https://www.govstack.global/) +3. [DHIS2 Security Team & Community of Practice](https://dhis2.org/security/) +4. [Asia eHealth Information Network (AeHIN) Communities of Practice](https://www.asiaehealthinformationnetwork.org/communities-of-practice/) diff --git a/i18n/es/docusaurus-plugin-content-docs/current/get-started/standards.md b/i18n/es/docusaurus-plugin-content-docs/current/get-started/standards.md new file mode 100644 index 000000000000..10d73a559b7b --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/get-started/standards.md @@ -0,0 +1,235 @@ +--- +sidebar_label: Estándares +title: Estándares y OpenFn +translation_source_hash: 4268e67655414b5830b8194dd7e98e23d283a613 +translation_review_status: machine +--- + +OpenFn sigue estándares globales para el software de código abierto y para las +soluciones de motor de workflows. Sigue leyendo para saber cómo cumple OpenFn +con estándares concretos. + +## Bien público digital {#digital-public-good} + +La [Digital Public Goods Alliance](https://digitalpublicgoods.net/) reconoce a +OpenFn como bien público digital, o "DPG" por sus siglas en inglés. + +:::info Definición de bien público digital + +Software de código abierto, datos abiertos, modelos de IA abiertos, estándares +abiertos y contenido abierto que respetan la privacidad y otras buenas prácticas +aplicables, no causan daño por diseño y son muy relevantes para alcanzar los +Objetivos de Desarrollo Sostenible (ODS) de la Agenda 2030 de las Naciones +Unidas + +::: + +Puedes leer más sobre el estándar DPG +[aquí](https://digitalpublicgoods.net/standard/). + +## Bien global para la salud {#global-good-for-health} + +OpenFn es una de las 36 aplicaciones de software que Digital Square reconoce +como +[Global Good for Health](https://wiki.digitalsquare.io/index.php/What_are_Global_Goods#:~:text=Digital%20Square%20Global%20Goods%20are,scale%2C%20are%20used%20across%20multiple). + +:::info Definición de bien global para la salud + +Un bien global de software de salud digital maduro es software libre y de código +abierto (FOSS), cuenta con el respaldo de una comunidad sólida, tiene una +estructura de gobernanza clara, se financia con varias fuentes, se ha desplegado +a una escala considerable, se usa en varios países, ha demostrado su eficacia, +está diseñado para ser interoperable y es una aplicación estándar emergente. + +::: + +Puedes leer más sobre los bienes globales para la salud +[aquí](https://digitalsquare.org/digital-health-global-goods). + +## Arquitectura estándar de OpenHIE {#openhie-standard-architecture} + +OpenFn se considera una tecnología de referencia de OpenHIE y cumple con la +arquitectura estándar de OpenHIE para implementaciones de salud digital. + +_Esta sección da por hecho que conoces la especificación de OpenHIE, un marco de +referencia que hace posible compartir datos de salud entre sistemas de +información mediante un intercambio de información de salud ("HIE", por sus +siglas en inglés). Para saber más, consulta la +[documentación de OpenHIE](https://guides.ohie.org/arch-spec/) y su +[comunidad](https://ohie.org/)._ + +### OpenFn y OpenHIE {#openfn-and-openhie} + +La plataforma OpenFn v2 ([OpenFn/lightning](https://github.com/OpenFn/)) es un +**_motor de workflows_** compatible con OpenHIE que se usa para (1) automatizar +procesos de negocio complejos que abarcan varios sistemas digitales (incluidos +los componentes de OpenHIE _y_ los sistemas del punto de atención) y (2) +gestionar el mapeo y la transformación de datos. + +Si tu organización implementa la arquitectura estándar de OpenHIE, OpenFn te +ofrece un motor de workflows que se comunica con tu capa de interoperabilidad +("IOL", por sus siglas en inglés). Con OpenFn puedes automatizar: + +- workflows entre sistemas del punto de servicio; +- workflows entre los componentes centrales del HIE; +- los pasos de transformación necesarios para preparar los datos antes de + enviarlos a otros componentes del HIE a través de la IOL. (Los workflows de + OpenFn son una alternativa a los "mediators" de OpenHIM que se puede usar y + gestionar desde una interfaz web). + +OpenFn cumple los +[requisitos funcionales](https://guides.ohie.org/arch-spec/openhie-component-specifications-1/openhie-interoperability-layer-iol#openhie-iol-functional-requirements) +de la IOL de OpenHIE, por lo que algunas organizaciones también usan OpenFn como +su capa de interoperabilidad central. Aun así, OpenFn todavía no puede usarse +como una **_capa de interoperabilidad_** totalmente compatible con OpenHIE, +porque no usa el perfil IHE ATNA (consulta el +[requisito IOL-WF1](https://guides.ohie.org/arch-spec/openhie-component-specifications-1/openhie-interoperability-layer-iol#openhie-iol-workflow-requirements)). + +![Arquitectura de OpenHIE](/img/openhie_architecture.webp) + +_Para ver un resumen de OpenFn Lightning y de cómo encaja en OpenHIE, mira +nuestra +[presentación para el OpenHIE showcase](https://www.youtube.com/watch?v=PTRRZBYtqyc)_ +o sigue leyendo para tener más contexto. + +### Más sobre cómo OpenFn cumple la especificación de OpenHIE {#more-on-how-openfn-supports-the-openhie-spec} + +#### La capa de interoperabilidad (IOL): {#the-interoperability-layer-iol} + +- Se sitúa entre los componentes de OpenHIE y los sistemas del punto de atención +- Sirve como punto de entrada único y pasarela segura a OpenHIE +- Cumple los requisitos de enrutamiento y auditoría de transacciones + +_OpenFn Lightning cumple los requisitos funcionales de la IOL, pero no es +totalmente compatible con OpenHIE, porque todavía no usa el perfil IHE ATNA_ + +#### El motor de workflows: {#the-workflow-engine} + +- Ofrece interfaces listas para usar que se conectan a los sistemas del punto de + atención +- Gestiona el mapeo y la transformación de datos complejos para darles el + formato que necesita el sistema de destino (por ejemplo, mapear datos de un + sistema del punto de atención al modelo de datos de un componente de OpenHIE, + mapear datos no FHIR a perfiles FHIR, o ambas cosas) +- Envía los datos a la capa de interoperabilidad +- Puede seguir el estado de la atención de un paciente a lo largo del tiempo y + actuar según ese contexto (por ejemplo, enviando alertas) para mejorar su + atención. + +_OpenFn Lightning es un motor de workflows compatible con OpenHIE_ + +### Caso práctico: OpenFn como mediator de OpenHIM {#case-study-openfn-as-an-openhim-mediator} + +En Nigeria, como parte del +[proyecto ALMANACH](https://articles.nigeriahealthwatch.com/almanach-revolutionising-the-management-of-childhood-illnesses-in-adamawa-state/), +SwissTPH usó OpenFn para automatizar el mapeo y el intercambio de datos entre +CommCare y DHIS2 para la vigilancia de enfermedades. El workflow funcionó +durante varios años en la nube de OpenFn. Para preparar el traspaso y la +ampliación de escala, el equipo de SwissTPH preparó después una integración +profunda con OpenHIM para un despliegue local. + +SwissTPH incorporó su workflow de OpenFn a su instancia de OpenHIM como +"mediator", de modo que todos los datos pasaran por esta IOL. Al mismo tiempo, +siguió aprovechando el adaptor de DHIS2 de OpenFn, listo para usar, y las +plantillas de workflow reutilizables para desarrollar rápidamente una +automatización que da formato a los datos recibidos de CommCare y los mapea al +modelo de datos de DHIS2. + +![SwissTPH](/img/swisstph.webp) + +## GovStack + +OpenFn cumple la +[especificación estándar de GovStack](https://govstack.gitbook.io/bb-workflow/2-description) +para motores de workflows. + +## Principios para el Desarrollo Digital {#principles-for-digital-development} + +OpenFn se diseñó para el sector social y ha dado prioridad activamente a los +[Principios para el Desarrollo Digital](https://digitalprinciples.org/) desde +sus inicios. + +Las soluciones de OpenFn: + +- **son interoperables** (conectan cualquier aplicación); +- **son reutilizables** (usa configuraciones de OpenFn existentes como + plantillas, o comparte, copia y modifica fácilmente tus propias + configuraciones; consulta docs.openfn.org/library); +- **son sostenibles** (opciones de implementación flexibles, sin dependencia de + un proveedor); +- **son escalables** (OpenFn usa tecnología de nivel empresarial para gestionar + grandes volúmenes de datos y ofrece varias opciones de despliegue para que la + solución sea totalmente tuya en cualquier servidor); +- **promueven los estándares abiertos y el acceso abierto** (mediante nuestro + software de código abierto, la documentación y funciones que ayudan a los + usuarios a implementar estándares abiertos en sus soluciones de intercambio de + información), y +- **abordan la privacidad y la seguridad**. + +## FHIR para el intercambio de datos de salud {#fhir-for-health-data-exchange} + +[FHIR](https://www.hl7.org/fhir/) (se pronuncia "fire", fuego en inglés 🔥) es +un estándar para el intercambio de datos de salud, publicado por HL7®. + +Las organizaciones de salud usan OpenFn para conectar varios sistemas, +compatibles con FHIR o no, de forma segura, estable y escalable. OpenFn puede +facilitar 2 categorías de workflows FHIR: + +### 1. Intercambio de datos de no FHIR a FHIR {#1-non-fhir-to-fhir-data-exchange} + +Los usuarios de OpenFn pueden configurar workflows que convierten datos no FHIR +a formatos compatibles con FHIR y luego los envían a sistemas FHIR. + +Por ejemplo, obtener datos de la aplicación móvil CommCare, convertirlos a FHIR +y enviarlos al almacén FHIR del sistema nacional de salud. +![Workflow de no FHIR a FHIR](/img/workflow_nonfhir_fhir.webp) + +### 2. Intercambio de datos de FHIR a FHIR {#2-fhir-to-fhir-data-exchange} + +Los usuarios de OpenFn también pueden configurar workflows para automatizar el +intercambio y el enrutamiento de datos _ya_ compatibles con FHIR hacia otros +sistemas compatibles con FHIR. + +Por ejemplo, obtener datos de la API FHIR de OpenMRS y reenviarlos al almacén +FHIR del sistema nacional de salud (sin necesidad de transformar los datos). + +![Workflow de FHIR a FHIR](/img/workflow_fhir_fhir.webp) + +## Adaptors de FHIR {#fhir-adaptors} + +Los [adaptors](/adaptors) de OpenFn agilizan la configuración de integraciones +con las aplicaciones de destino (¡incluidos los endpoints FHIR!). Actualmente, +el equipo principal trabaja en un conjunto de adaptors específicos de FHIR para +hacer posible la interoperabilidad con sistemas FHIR. + +El [adaptor fhir-4](/adaptors/fhir-4) facilita el acceso y la modificación de +datos alojados en cualquier servidor compatible con +[FHIR r4](https://www.hl7.org/fhir/R4/). También ofrece asistencia de código +completa a los desarrolladores mientras crean definiciones de recursos +concretas, lo que simplifica la introducción de datos y la lógica de mapeo. + +También ofrecemos un adaptor genérico, [fhir](/adaptors/fhir), compatible con +todas las versiones de FHIR. + +:::info Compatibilidad con FHIR 4 + +El adaptor `fhir-4` es nuevo en OpenFn desde marzo de 2025. Ofrece un nivel de +compatibilidad más completo que el adaptor genérico [fhir](/adaptors/fhir). La +compatibilidad con otras versiones de FHIR llegará pronto + +::: + +Consulta la +[wiki de adaptors](https://github.com/OpenFn/adaptors/wiki/Generating-Fhir-Adaptors) +para aprender a crear tu propio adaptor de FHIR específico para la guía de +implementación de FHIR que uses + +## Otros estándares de datos {#other-data-standards} + +Los workflows de OpenFn pueden automatizar reglas de transformación, limpieza y +formato de datos para garantizar el cumplimiento de los estándares específicos +de _tu_ organización. + +Pregunta en la [comunidad](https://community.openfn.org) cómo puede usarse +OpenFn para ayudar a automatizar la aplicación y el cumplimiento de otros +estándares de datos. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/get-started/terminology.md b/i18n/es/docusaurus-plugin-content-docs/current/get-started/terminology.md new file mode 100644 index 000000000000..188a3b86b969 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/get-started/terminology.md @@ -0,0 +1,310 @@ +--- +title: Conceptos clave +translation_source_hash: 2885a84bfad099adf8223a97458e5d5dbe03e5b4 +translation_review_status: machine +--- + +En todo el OpenFn Integration Toolkit y en este sitio de documentación +encontrarás términos propios de OpenFn que es importante entender. Esta página +es tu guía de referencia: un glosario de las palabras _propias de OpenFn_ más +importantes y de lo que significan. + +:::tip ¿Falta algo? + +Si encontraste una palabra, una expresión o un concepto que crees que falta en +esta página, abre un issue en [OpenFn/docs](https://github.com/OpenFn/docs), +sugiere un cambio en +[esta página](https://github.com/OpenFn/docs/blob/main/docs/get-started/terminology.md) +o pregunta en la [comunidad](https://community.openfn.org) + +::: + +Si buscas un glosario de términos genéricos de integración de datos (y no de +estos términos _propios de OpenFn_), ve a la página +[Glosario de integración](/documentation/get-started/glossary) de la sección +Primeros pasos. Si no, ¡sigue leyendo! + +## Proyecto {#project} + +Un proyecto es una agrupación administrativa en OpenFn, parecida a un "espacio +de trabajo". + +En la plataforma (OpenFn/lightning), los proyectos definen quién puede acceder a +la configuración y al historial de tus workflows de OpenFn. Cada proyecto tiene +un propietario y uno o más colaboradores. + +En el despliegue y el desarrollo locales, un proyecto también corresponde a un +archivo [`project.yaml`](/documentation/deploy/portability-versions#v2), que +define su configuración. + +En ambos casos, un proyecto contiene workflows, triggers, credenciales y todo lo +que necesitas para automatizar e integrar con OpenFn. + +## Workflow + +:::tip + +¡Los workflows son la parte de **"qué hacer"** de la automatización! + +::: + +Un workflow es una secuencia estructurada de tareas, procesos o acciones que se +ejecutan automáticamente según reglas, disparadores y lógica predefinidos. + +Al trabajar con IA, los workflows aportan la ejecución estructurada que hace +falta para convertir lo que propone un LLM en acciones reales, mientras que los +agentes de IA permiten tomar decisiones más dinámicas dentro de los workflows. + +Un workflow es un conjunto formado por un trigger, steps, paths y lógica +personalizada, conectados entre sí para automatizar un proceso de negocio o una +tarea concretos. Se configura en el Canvas de la aplicación web, o localmente +con código. + +La automatización de OpenFn gira en torno a los +[workflows](/documentation/build/workflows), que pueden tener uno o varios +steps. Un workflow puede ejecutarse en tiempo real (a partir de un evento, por +ejemplo, el registro de un paciente nuevo), de forma programada (por ejemplo, +todos los días a las 8 a. m.) o manualmente, cuando lo necesites. + +Piensa en un workflow como un conjunto de instrucciones que le darías a alguien +de tu equipo. Por ejemplo: crea un registro de paciente nuevo en OpenMRS cuando +llegue desde CommCare un formulario con un cliente recién registrado; exporta +datos a DHIS2 todas las semanas, los viernes a las 11 p. m.; envía un SMS con el +número de confirmación del pago cuando llegue el mensaje de confirmación del +pago. + +Los workflows más comunes automatizan: + +- Reportes para un seguimiento de programas mejor y más rápido (sobre todo + reportes de dispositivos móviles a un MIS) +- Pasos rutinarios de ETL (extracción, transformación y carga) y de limpieza de + datos +- Alertas (SMS, correo electrónico) +- Derivaciones entre sistemas de socios +- Asignaciones o aprobaciones de tareas +- Reportes de quejas o de casos +- Transacciones financieras o pagos + +:::note Los workflows son reutilizables + +Los workflows son totalmente configurables y reutilizables. También pueden +encadenarse para automatizar procesos de varios pasos y sincronizaciones de +datos en ambos sentidos, que mantienen la coherencia de los datos entre varias +aplicaciones (con patrones Saga de varias aplicaciones). + +::: + +### Adaptor + +:::tip + +¡Los adaptors son la parte de **"dónde hacerlo"** de la automatización! + +::: + +Los [adaptors](/adaptors) de OpenFn son módulos de código abierto que dan a tus +workflows las funciones que necesitan para comunicarse con la API de un sistema +concreto. Algunos ejemplos son [dhis](/adaptors/dhis2), +[`postgresql`](/adaptors/postgresql) y [`http`](/adaptors/packages/http-docs). +Actualmente hay más de 70 adaptors activos, y cualquiera puede crearlos o +mejorarlos. El código fuente está en +[GitHub/Adaptors](https://github.com/OpenFn/adaptors). + +### Credencial {#credential} + +:::tip + +¡Las credenciales son la parte de **"cómo iniciar sesión"** de la +automatización! + +::: + +Una credencial sirve para autenticarse en una aplicación de destino (por +ejemplo, el nombre de usuario, la contraseña y la URL de inicio de sesión de una +base de datos) para que un step de un workflow pueda ejecutarse. El modelo de +seguridad de OpenFn guarda las credenciales separadas de los workflows, para que +los nombres de usuario y las contraseñas almacenados (todos cifrados) no se +filtren ni lleguen a las personas equivocadas. + +## Trigger + +:::tip + +¡Los triggers son la parte de **"cuándo hacerlo"** de la automatización! + +::: + +Un [trigger](/documentation/build/triggers) determina **cómo y cuándo** deben +ejecutarse automáticamente los workflows (por ejemplo, en tiempo real o de forma +programada). Al activarse, el trigger crea una nueva +[work order](/documentation/get-started/terminology#work-order) y ejecuta el +workflow. + +Configura un trigger de tipo "Webhook Event" si quieres que tu workflow se +ejecute en tiempo real cuando ocurre un evento en una aplicación externa (por +ejemplo, cuando se envía un formulario nuevo o llega una notificación nueva). + +Configura un trigger de tipo "Cron" si quieres que tu workflow se ejecute según +un calendario concreto (por ejemplo, todos los días a las 8 a. m., o el primer +lunes de cada mes). + +## Work order + +:::tip + +Las work orders registran **"cuándo y qué disparó"** la automatización, y nos +ayudan a ver si el workflow se completa correctamente y cuándo. + +::: + +Una work order es una solicitud para ejecutar un workflow con una entrada +determinada (por ejemplo, un formulario recién enviado o un registro de paciente +que hay que procesar). + +Se crea una work order cada vez que se activa el trigger de un workflow, o +cuando un usuario administrador la crea manualmente. + +Para completarse correctamente, la work order debe llegar sin errores a un step +final; así se garantiza que el procesamiento terminó. Puede que hagan falta +varios "runs" del workflow para que una work order se considere correcta. + +Las work orders permiten seguir de cerca si un workflow procesa correctamente +cada entrada (por ejemplo, "registro de paciente 123"), como en una auditoría de +"gestión de casos". + +Imagina que un workflow está configurado para crear un paciente nuevo en OpenMRS +cada vez que se abre un caso nuevo en CommCare. Si durante la semana siguiente +se abren 5 casos en CommCare, verás 5 work orders distintas para este workflow. +Si 4 work orders son correctas y una falla, verás 4 pacientes nuevos en OpenMRS, +y el administrador de tu sistema habrá recibido un aviso de que no se pudo crear +uno de esos pacientes (o se pondrá en marcha el manejo de errores más robusto +que hayas configurado). + +![Work order](/img/work_order_shot.webp) + +:::note + +Normalmente hay una correspondencia de 1 a 1 entre las work orders y las cosas +reales con las que trabajas. Por ejemplo, podrías crear un workflow que obtenga +de DHIS2 todos los datos de eventos actualizados de las últimas 2 semanas y los +publique en un mapa público con CartoDB. Este workflow se disparará a intervalos +definidos, en este caso cada 2 semanas, así que al cabo de un mes solo verás 2 +work orders en OpenFn (una cada dos semanas). Cada work order tendrá un estado +correcto o fallido, con runs relacionados que registran los detalles de cada +transacción y cuántos registros de eventos se procesaron. + +::: + +## Run + +:::tip + +¡Los runs registran **"lo que pasó"** en la automatización! + +::: + +Un run es un intento individual de ejecución para completar una work order. +Puede haber varios runs de un workflow para una misma work order (porque el +primer run puede fallar y hay que reintentarlo para procesarla correctamente). + +Los runs tienen hora de inicio, hora de fin, logs y códigos de estado que +indican cuándo tuvieron lugar, qué hicieron y si tuvieron éxito o no. + +![Canvas de workflow de OpenFn](/img/run_view_logs.webp) + +Imagina que un workflow está configurado para crear un paciente nuevo en OpenMRS +cada vez que se abre un caso nuevo en CommCare. Si hoy se crea 1 paciente: + +- Se creará 1 work order en OpenFn, que disparará un run para crear el paciente + en OpenMRS. +- Si ese run falla por un error (por ejemplo, la contraseña del usuario de + OpenMRS es incorrecta o al paciente le falta información obligatoria), el + "Status" de ese run y de la work order relacionada aparecerá como `failed`. +- Los usuarios de OpenFn pueden corregir el error y elegir "rerun" para volver a + ejecutar ese run fallido. Esto crea un 2.º run relacionado con la work order + original. Si tiene éxito, el "Status" del 2.º run y de la work order aparecerá + como "success". + +### Logs + +Los logs son los registros que genera el motor de ejecución de workflows para +dejar constancia de lo que se hizo al ejecutar un workflow o un step concreto. + +Los desarrolladores de OpenFn pueden controlar lo que aparece en los logs +editando las sentencias `console.log(...)` en las expresiones de job de cada +step. + +![Logs](/img/logs_run.webp) + +## History + +En la plataforma, la página History muestra una lista de todas las work orders y +todos los runs que se procesaron en un proyecto. + +![History](/img/case-referral-history.webp) + +## Inspector + +En la plataforma, el Inspector es la interfaz que permite editar, probar y +ejecutar workflows. + +El Inspector tiene 3 paneles clave: `Input`, `Editor` y `Output`. + +![Inspector](/img/inspector_interfaces.webp) + +### Input + +El Input son los datos (`json`) que un step de un workflow usa como entrada +inicial cuando se ejecuta. Cada run tiene un Input (el `state` inicial) y un +Output (el `state` final). + +Los Inputs pueden crearse automáticamente a partir de un evento de webhook (por +ejemplo, un mensaje reenviado o un payload JSON enviado a OpenFn) o de otro step +del workflow, o manualmente, por un usuario de OpenFn. + +Ejemplo de Input a partir de un formulario enviado desde una aplicación móvil de +recolección de datos (como Kobo, ODK o CommCare): + +```json +{ + "data": { + "form": { + "@name": "Register New Patient", + "case": { + "@case_id": "a9bX12c", + "@date_modified": "2021-01-21T07:08:19.431000Z", + "@user_id": "aaa", + "@xmlns": "http://commcarehq.org/case/transaction/v2", + "create": { + "case_name": "John Doe", + "age": 16, + "case_type": "patient", + "owner_id": "alan.worker" + } + } + } + } +} +``` + +### Output + +El Output son los datos finales (`json`) que produce un step de un workflow, +según la lógica de negocio definida en su expresión de job. El Output pasa al +siguiente step del workflow, a la aplicación de destino conectada, o a ambos. + +Ejemplo de Output si el formulario del ejemplo anterior se mapeara a una +aplicación de gestión de casos conectada: + +```json +{ + "data": { + "patient": { + "full_name": "John Doe", + "age_at_enrollment": 16, + "type": "new", + "source": "mobile-app" + } + } +} +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/get-started/try-out.md b/i18n/es/docusaurus-plugin-content-docs/current/get-started/try-out.md new file mode 100644 index 000000000000..f446638383c7 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/get-started/try-out.md @@ -0,0 +1,60 @@ +--- +title: Prueba la v2⚡ +id: try-out +sidebar_label: Prueba la v2⚡ +translation_source_hash: 4e28f15f405f837836a724296995294f1761e78d +translation_review_status: machine +--- + +Si te interesa probar OpenFn v2⚡ hoy mismo, tienes 3 opciones: + +## 1. Regístrate para obtener una cuenta gratuita {#1-register-for-a-free-account} + +Regístrate para obtener una cuenta gratuita en el servicio alojado de OpenFn.org +y crea tu propio proyecto privado. Para hacerlo, visita: +[www.openfn.org/register](https://www.openfn.org/register) + +Ten en cuenta que esta cuenta gratuita tiene límites. Mejora tu plan para +acceder a más funciones en la plataforma alojada y segura de OpenFn. Más +información en [nuestro sitio web](https://www.openfn.org/pricing). + +:::tip ¿Ya tienes una cuenta? + +Visita [www.openfn.org/login](https://www.openfn.org/login) para iniciar sesión +en tu cuenta de Lightning v2. Si solo tienes un usuario de v1, tendrás que crear +una cuenta nueva de v2 en +[www.openfn.org/register](https://www.openfn.org/register). + +::: + +## 2. Inicia sesión en el sitio de demostración de OpenFn {#2-log-into-the-openfn-demo-site} + +Visita [demo.openfn.org](https://demo.openfn.org) e inicia sesión con estas +credenciales para explorar la plataforma: + +- nombre de usuario: `demo@openfn.org` +- contraseña: `welcome12345` + +:::warning + +El sitio de demostración se restablece cada 24 horas y se pierde cualquier +cambio de configuración que hagas. No lo uses para ninguna configuración que +quieras conservar. + +::: + +## 3. Instala OpenFn/lightning localmente {#3-install-openfnlightning-locally} + +Instala OpenFn v2 localmente para acceder al software de código abierto y +explorar sin límites. La documentación para desarrolladores está en nuestro +repositorio de GitHub: +[github.com/OpenFn/lightning](https://github.com/OpenFn/lightning). + +:::info ¿Tienes preguntas? + +Consulta esta documentación para ver más detalles sobre funciones concretas +(mira el menú lateral), explora la +[página principal de la documentación](/get-started/home.md) o publica tus +preguntas en la [comunidad](https://community.openfn.org). + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/hosted/overview.md b/i18n/es/docusaurus-plugin-content-docs/current/hosted/overview.md new file mode 100644 index 000000000000..d88e2b8fdda5 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/hosted/overview.md @@ -0,0 +1,173 @@ +--- +title: Gestión de facturación y suscripciones en OpenFn.org +id: overview +sidebar_label: Suscripciones (OpenFn.org) +translation_source_hash: d1a4431aa8938f214a646b619a889fa8e0467f9b +translation_review_status: machine +--- + +## Resumen {#overview} + +[OpenFn.org](https://www.openfn.org) ofrece un despliegue de OpenFn seguro, +estable, escalable y alojado en la nube, como una opción SaaS lista para usar y +bajo demanda que puede resultar más rentable que gestionar tu propio despliegue +local. + +Ve a **[openfn.org/pricing](https://www.openfn.org/pricing)** para conocer +nuestros planes y servicios, o sigue leyendo para saber cómo gestionar las +cuentas de facturación y las suscripciones. + +:::tip ¿Necesitas OpenFn en _tus_ servidores? + +OpenFn se puede desplegar en cualquier lugar. Consulta la +[documentación de despliegue](/deploy/options.md) para conocer las opciones +compatibles y la vía de despliegue local "hazlo tú mismo". Si buscas ayuda +experta para gestionar tu despliegue local, consulta los +[servicios de despliegue gestionado](https://www.openfn.org/pricing?hostingType=selfHosted) +que ofrece el equipo principal de OpenFn. + +::: + +## Registro de usuarios {#user-registration} + +Cuando creas una cuenta de usuario en la nube desde OpenFn.org/signup, obtienes +acceso inmediato a un único proyecto del plan gratuito, y creamos una cuenta de +facturación personal (consulta [más abajo](#billing-accounts)) que luego puedes +usar para comprar project spaces adicionales o mejorar tu plan gratuito. + +## Cuentas de facturación {#billing-accounts} + +Todos los proyectos _pertenecen_ a una única cuenta de facturación. La cuenta de +facturación contiene: + +- **Suscripciones de proyectos** (tus proyectos, cada uno con su suscripción); +- **Métodos de pago** (tarjetas de crédito y métodos de pago por factura); y +- **Usuarios de la cuenta de facturación** (los usuarios de OpenFn que pueden + ver o gestionar esta cuenta de facturación) + +### Usuarios de la cuenta de facturación {#billing-account-users} + +De forma predeterminada, eres el propietario de tu propia cuenta de facturación +personal. Puedes invitar a otros **administradores** para que agreguen o +modifiquen métodos de pago y cambien la suscripción de un proyecto. También +puedes agregar **lectores**, que pueden ver las suscripciones de proyectos de tu +cuenta, los métodos de pago y los demás usuarios de la cuenta de facturación, +pero no pueden modificar nada de eso. + +## Gestionar suscripciones y métodos de pago {#manage-subscriptions-and-payment-methods} + +Sigue leyendo o mira el siguiente video para saber cómo gestionar tus +suscripciones y métodos de pago. + + + +### Métodos de pago {#payment-methods} + +Para mejorar la suscripción de tu proyecto, necesitas un método de pago +aprobado. Se admiten métodos de pago por "factura" y con "tarjeta de crédito". + +Para gestionar tus métodos de pago, haz clic en el menú `Billing Account` (si +estás en la lista de proyectos o en la página de perfil de usuario) o en el menú +`Subscription` (si estás en la página del proyecto o del workflow). Desde ahí, +haz clic en `Payment Methods` en el menú lateral. + +#### Pago por factura {#invoice-payment} + +Cuando agregas un método de pago por factura, tienes que esperar a que el equipo +de OpenFn.org lo apruebe antes de poder usarlo. + +:::tip Agregar un método de pago por factura + +Para agregar un método de pago por factura, haz clic en el botón "Add a new +invoice method" de la página de métodos de pago. Se abrirá un formulario que +puedes completar y enviar. Cuando hayas enviado el formulario correctamente, tu +solicitud de pago por factura aparecerá en la lista marcada como `pending`. + +Alguien del equipo de facturación de OpenFn se pondrá en contacto contigo para +verificar la información antes de aprobar el método de pago. + +_Ten en cuenta que solo puedes tener UN método de pago por factura pendiente a +la vez._ + +::: + +#### Pago con tarjeta de crédito {#credit-card-payment} + +Cuando agregas una tarjeta de crédito como método de pago, Stripe.com verifica +los datos de la tarjeta de inmediato y puedes mejorar tus suscripciones en ese +mismo momento. + +:::tip Agregar una tarjeta de crédito como método de pago + +Para agregar una tarjeta de crédito como método de pago, haz clic en el botón +"Add a new card" de la página de métodos de pago. Se abrirá un formulario en el +que puedes completar los datos de tu tarjeta. Stripe.com verificará tu tarjeta +de inmediato y podrás usarla para mejorar tu suscripción o crear una nueva +suscripción de proyecto. + +Si no consigues agregar tu tarjeta, contáctanos en support@openfn.org e indica +el error (si lo hay). + +::: + +### Planes y límites {#plans--limits} + +La lista completa de planes y límites está disponible en +[openfn.org/pricing](https://www.openfn.org/pricing). + +### Mejorar tu suscripción {#upgrading-your-subscription} + +:::warning Necesitas un método de pago válido + +Asegúrate de tener un método de pago válido antes de intentar mejorar tu +suscripción. Consulta la sección [Métodos de pago](#payment-methods) para más +detalles. + +::: + +Para mejorar tu suscripción: + +1. Haz clic en "Subscription" en el panel de tu proyecto +2. Haz clic en "Manage Subscription" +3. Selecciona un plan de la lista y baja para agregar runs adicionales si los + runs incluidos en tu plan no alcanzan para tu proyecto. _(Te recomendamos + usar el control deslizante para fijar el valor)_ +4. Selecciona un método de pago para pagar la mejora. +5. Baja hasta el final de la página para revisar los cambios en tu suscripción. +6. Haz clic en "Update subscription" + +Al mejorar el plan, se te cobrará de inmediato la _diferencia_ entre tu plan +actual y el nuevo. Al final de tu ciclo, el siguiente cargo será solo el costo +del nuevo plan. + +### Bajar el plan de tu suscripción {#downgrading-your-subscription} + +Al bajar de plan, puedes seguir usando tu plan actual hasta que termine el +ciclo, porque ya pagaste por adelantado el uso de ese ciclo. Cuando termina el +ciclo, se aplican los límites más bajos y el siguiente cargo será por el precio +del nuevo plan. + +## Transferir suscripciones de proyectos {#transferring-project-subscriptions} + +Si eres administrador de una cuenta de facturación, puedes solicitar transferir +la titularidad de un proyecto de tu cuenta de facturación (es decir, pedirle a +otra persona que lo pague). Escribe su correo electrónico y recibirá un aviso. + +Para aceptar la solicitud de transferencia, esa persona tiene que ser +administradora de al menos una cuenta de facturación con un método de pago +activo. Elegirá la cuenta de facturación y el método de pago que quiere usar, y +entonces la transferencia se completará y será _ella_ quien pague la próxima vez +que haya que pagar esa suscripción. + +## Cómo encaja todo (para los ingenieros 🤓) {#how-it-fits-together-for-the-engineers-} + +```mermaid +erDiagram + "User" }|--|{ "Project" : "A user can access many projects" + "User" }|--|{ "Billing Account" : "A user can access many billing accounts" + "Project" ||--|| "Subscription" : "A project has one active subscription" + "Billing Account" ||--o{ "Payment Method" : "A billing account has many payment methods" + "Payment Method" ||--o{ "Subscription" : "A payment method can be used for many subscriptions" + "Billing Account" ||--o{ "Subscription" : "A billing account has many subscriptions" + "Plan" ||--o{ "Subscription" : "Many subscriptions use the same plan" +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/best-practices.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/best-practices.md new file mode 100644 index 000000000000..8a765a61d5af --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/best-practices.md @@ -0,0 +1,108 @@ +--- +sidebar_label: Buenas prácticas +title: Buenas prácticas +translation_source_hash: db347090195ee9bfd115330405094e81c8cf3cef +translation_review_status: machine +--- + +## Usar secretos de credenciales en el código del job {#referencing-credential-secrets-in-your-job-code} + +Si tienes que usar secretos de credenciales en el código del job, puedes mapear +claves desde tu `state.configuration`. El ejemplo de abajo mapea de forma +dinámica el usuario y la contraseña de tu `state.configuration` (o de la +"credencial", si usas la app) al cuerpo de tu solicitud HTTP. + +```js +post('/api/v1/auth/login', { + body: { + username: $.configuration.username, //map the UN from credential + password: $.configuration.password, //map the PW from credential + }, + headers: { 'content-type': 'application/json' }, +}); +``` + +> **Nota:** Aunque la mayoría de los adaptors manejan la autenticación de forma +> automática, el adaptor `@openfn/language-common` permite manejarla a mano. + +**Enfoque recomendado:** en lugar de acceder a las credenciales desde el código +del job, deberías: + +1. Usar un step con un adaptor específico (por ejemplo, `@openfn/language-http`) + que tenga su propia credencial para la autenticación. +2. Agregar después un step con `@openfn/language-common` si necesitas + transformar más los datos. + +:::info OpenFn elimina la configuración y las funciones del state final + +OpenFn elimina automáticamente la clave `configuration` y cualquier función de +tu state final, y también de los logs si ejecutas workflows en la app. Así ayuda +a mantener seguros los secretos de tus credenciales y a evitar que se filtren en +History. + +::: + + + +## Manejo de errores {#error-handling} + +Si algo sale mal, normalmente lo mejor es dejar que tus jobs fallen. + +Un job que falla genera el estado correcto en la app de OpenFn y avisa de que +algo anda mal. + +No te preocupes, ¡los errores pasan todo el tiempo! Incluso los workflows más +consolidados lanzan algún error de vez en cuando por datos inesperados en algún +punto del proceso. Es parte de la vida; lo más importante es enterarse. + +Los errores deberían lanzarse desde el job sin mucha ceremonia: el runtime los +atrapa y los procesa como corresponde. + +Si un job lanza un error, este se registra en el log y se escribe en el state +final, así que debería ser fácil encontrarlo e identificar la causa. + +En un workflow, es habitual dejar que un job falle y luego hacer alguna tarea, +como enviar un correo a un administrador del sistema para avisarle del problema. + +Al procesar lotes de datos, quizás quieras atrapar los errores de cada elemento +y escribirlos en el state. Así, un elemento con problemas no arruina todo el +lote, y sabes qué elementos funcionaron y cuáles fallaron. Después puedes lanzar +una excepción para indicar que el job falló. + +## Escribir funciones que se puedan probar {#writing-testable-functions} + +Para que las funciones de un workflow de OpenFn se puedan probar, saca la lógica +real (mapeo, validación, formato, filtrado) de los bloques de operaciones y +ponla en funciones auxiliares puras que reciban entradas simples y devuelvan +salidas simples, sin depender de `state`, de variables globales ni de sistemas +externos. Exporta cada función auxiliar y deja que las operaciones solo pasen +los datos del state a esas funciones: + +```js +// workflows/patient-sync/transform.js +export function isValid(record) { + return Boolean(record.first_name && record.birth_date); +} + +export const mapPatient = record => ({ + name: `${record.first_name} ${record.last_name ?? ''}`.trim(), + dob: record.birth_date, + sex: record.gender?.toLowerCase() === 'f' ? 'female' : 'male', +}); + +fn(state => ({ + ...state, + data: state.data.filter(isValid).map(mapPatient), +})); +``` + +Para probar las funciones auxiliares exportadas, consulta +[Pruebas unitarias de jobs](/documentation/jobs/unit-testing-jobs). diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/compilation.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/compilation.md new file mode 100644 index 000000000000..a6cd033c14d3 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/compilation.md @@ -0,0 +1,53 @@ +--- +sidebar_label: Compilación +title: Compilación +translation_source_hash: 84aca9c4f6f2ddcb2a23ffb5806b86f9ce720c0d +translation_review_status: machine +--- + +## Compilación {#compilation} + +Técnicamente, el código que escribes no es JavaScript ejecutable. No puedes +simplemente ejecutarlo con Node.js. Hay que transformarlo, o compilarlo, en +código JS estándar y portable. + +:::warning + +Este es un tema avanzado, pensado sobre todo para desarrolladores de JavaScript +y para quienes sienten curiosidad técnica. Esta documentación no pretende ser +completa: solo es un empujón en la dirección correcta para ayudarte a entender +cómo funcionan los jobs. + +::: + +Las principales diferencias entre el código de OpenFn y JavaScript son: + +- Las funciones del nivel superior del código se ejecutan de forma síncrona (en + secuencia), aunque contengan código asíncrono. +- El código de OpenFn no contiene instrucciones import (aunque técnicamente + puede tenerlas). Estas se agregan al compilar. +- El código compilado es un módulo ESM de JavaScript que exporta por defecto un + array de funciones async. El runtime importa y ejecuta estas funciones. + +No debería hacer falta entender la compilación en detalle, pero deberías saber +que el código que escribes no es el código que se ejecuta. + +Si eres desarrollador de JavaScript, entender algunos de estos cambios podría +ayudarte a comprender mejor cómo funciona OpenFn. Con la CLI, puedes ejecutar +`openfn compile path/to/job.js -a ` para ver el código compilado. + +Este es un ejemplo de cómo queda un job sencillo al compilarlo: + +Este job: + +```js +get('/patients'); +``` + +Se compila en este módulo de JavaScript: + +```js +import { get } from '@openfn/language-http'; +export * from '@openfn/language-http'; +export default [get('/patients')]; +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/data-transformation.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/data-transformation.md new file mode 100644 index 000000000000..f97b4304bd9f --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/data-transformation.md @@ -0,0 +1,199 @@ +--- +sidebar_label: Transformación de datos +title: Transformación de datos +translation_source_hash: a4731145379c6ab801d158a4696762e407360d24 +translation_review_status: machine +--- + +## Mapeo de objetos {#mapping-objects} + +Un caso de uso común de `fn` en OpenFn es mapear, convertir o transformar un +objeto del sistema A al formato del sistema B. + +A menudo lo hacemos en varios jobs del mismo workflow, para poder usar distintos +adaptors. Pero en este ejemplo trabajaremos con tres operaciones en un solo job +con el adaptor http: una para obtener los datos, otra para transformarlos y otra +para subirlos: + +```js +// Fetch an object from one system +get('https://www.system-a.com/api/patients/123'); + +// Transform it +fn(state => { + // Read the data we fetched + const obj = state.data; + + // convert it by mapping properties from one object to the other + state.uploadData = { + id: obj.id, + name: `${obj.first_name} ${obj.last_name}`, + metadata: obj.user_data, + }; + + // Don't forget to return state! + return state; +}); + +// Post it elsewhere +post('https://system-b.com/api/v1/records/123', (state) => state.uploadData); +``` + +:::tip Conversiones por lotes + +Estos ejemplos muestran cómo convertir un solo objeto, pero a veces tenemos que +convertir muchos objetos a la vez. + +Consulta el ejemplo de each() más abajo para ver cómo hacerlo con el operador +each. + +También puedes usar una función map() o forEach() de JavaScript dentro de un +callback o de un bloque `fn`. + +En general, es más fácil repartir la lógica del job en muchas operaciones de +nivel superior, cada una a cargo de una tarea, que tener unas pocas operaciones +muy anidadas. + +::: + +Esto está bien. De hecho, tener muchas operaciones que hacen cada una una tarea +pequeña es una buena práctica. Hace que el código sea más legible y fácil de +probar, y también más fácil de entender y de depurar cuando algo sale mal. + +Sin embargo, todos los argumentos de una operación aceptan una función (lo que +permite referencias diferidas al state, como se describió antes), así que +podemos hacer la conversión directamente en la operación post, por ejemplo: + +```js +// Fetch an object from one system +get('https://www.system-a.com/api/patients/123'); + +// Transform and post it elsewhere +post('https://system-b.com/api/v1/records/123', state => ({ + id: state.data.id, + name: `${state.data.first_name} ${state.data.last_name}`, + metadata: state.data.user_data, +})); +``` + +Usar bien estas funciones de resolución diferida es fundamental para escribir +buenos jobs de OpenFn. + +## Iteración con each() {#iteration-with-each} + +Un caso de uso muy común en la integración de datos es convertir datos de un +formato a otro. Normalmente, esto implica recorrer un array de elementos, +convertir los valores y mapearlos a un array nuevo. + +En OpenFn, podemos usar el operador `each()` para hacerlo. + +```js +each( + '$.data.items[*]', + get(state => `/patients/${state.data.id}`) +); +``` + +El operador `each()` recibe como primer argumento una cadena de JSON path, que +apunta a alguna parte del state. En JSON path, usamos `$` para referirnos a la +raíz, la notación de punto para encadenar una ruta y `[*]` para "seleccionar" un +array de elementos. El segundo argumento es una operación, que recibe cada +elemento al final del JSON path como `state.data`, pero por lo demás recibe el +resto del objeto state. + +Así podemos recorrer cada elemento y volver a escribirlo en el state, así: + +```js +fn((state) => { + // Initialize an array into state to use later + state.transformed = [] + return state; +}) +each("$.items[*]", fn(state) => { + // Pull the next item off the state + const next = state.data; + + // Transform it + const transformed = { ...next }; + + // Write it back to the top-level transformed array on state + state.transformed.push(transformed) + + // Always return state + return state; +}) +``` + +O podemos pasarle otra operación, como en este ejemplo de Salesforce: + +```js +each( + '$.form.participants[*]', + upsert('Person__c', 'Participant_PID__c', state => ({ + Participant_PID__c: state.pid, + First_Name__c: state.participant_first_name, + Surname__c: state.participant_surname, + })) +); +``` + +Cada participante se inserta o actualiza en Salesforce (`upserted`), con sus +campos de Salesforce mapeados a los valores del array `participants`. + +:::info JSON paths + +Usar una cadena de JSON path como primer argumento de `each()` permite que el +runtime evalúe de forma diferida el valor de esa ruta. +[Consulta Leer el state de forma diferida](/jobs/operations.md#reading-state-lazily). + +No todas las operaciones admiten una cadena de JSON path. Consulta la +[documentación de cada adaptor](/adaptors) para orientarte. + +::: + +## Inicialización de variables {#variable-initialisation} + + + +Es habitual tener que declarar algunas variables al inicio del job. Pueden ser +valores estáticos para usar más adelante, funciones que se llaman varias veces a +lo largo del job o partes del state que queremos devolver al final. + +Se considera una buena práctica usar un bloque `fn()` para hacerlo al inicio del +job, creando propiedades personalizadas en el state, por ejemplo: + +```js +fn(state => { + // Create an array to hold the final results of the job + state.results = []; + + // Create a lookup index to be used during the job + state.lookup = {}; + + state.keyMap = { + AccountName: 'C__Acc_Name', // capture various static mappings for transformation + }; + + state.maxPageSize = 200; // Define some config options + + state.convertToSF = item => { + /* ... */ + }; // A function to be re-used + + return state; +}); + +// the rest of your job code goes here +get('/abc'); + +fn(state => { + /* ... */ + + // Only return the results array as output from the job + return { result: state.results }; +}); +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/image-handling.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/image-handling.md new file mode 100644 index 000000000000..2ef7e67eee2f --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/image-handling.md @@ -0,0 +1,118 @@ +--- +title: Manejo de imágenes +translation_source_hash: 0edbfa9276676e40ecf010f6dd237f915e249426 +translation_review_status: machine +--- + +Los jobs de OpenFn se ejecutan en JavaScript y, en la mayoría de los casos, +manejan datos JSON de API REST o de webhooks. Recibimos JSON, lo transformamos +con JavaScript y luego lo enviamos a otra API REST. Sin embargo, a veces +necesitas trabajar con imágenes u otros archivos binarios. En esta página te +explicamos cómo hacerlo. + +:::success En resumen: + +Las imágenes y otros archivos binarios, en general, **_simplemente +funcionan™️_**. Los casos poco comunes podrían requerir cambios en los adaptors. + +::: + +:::info Manipulación avanzada de imágenes + +¿Necesitas cambiar el tamaño de una imagen, comprimirla, quitarle o agregarle +metadatos EXIF, o leer sus metadatos? Usa el +[adaptor `image-utils`](/adaptors/packages/image-utils-docs), que ejecuta estas +operaciones de forma nativa en tu job, sin necesidad de un microservicio +externo. Consulta +[Manipulación de imágenes con el adaptor `image-utils`](#image-manipulation-with-the-image-utils-adaptor) +más abajo para ver los detalles. + +- **Sin acceso a binarios externos**: los jobs de la plataforma siguen + ejecutándose en un entorno aislado de Node.js y no pueden invocar programas + externos como `imagemagick` o `ffmpeg`. El adaptor `image-utils` funciona + completamente dentro del entorno de ejecución de Node.js, así que no los + necesita. +- **Archivos grandes**: Base64 aumenta mucho el tamaño del payload, así que + evítalo con archivos grandes siempre que puedas. Es mejor trabajar con Buffers + (el formato que devuelven por defecto las operaciones de `image-utils`). + +::: + +## Base64 (manejo estándar) {#base64-standard-handling} + +En esencia, para trabajar con imágenes, PDF u otros archivos, guardarlos en +`state` y pasarlos de un step a otro en un workflow de OpenFn, hay que +codificarlos en base64 y luego volver a convertirlos en Buffers antes de +enviarlos a la API de un sistema de destino. + +El adaptor HTTP ya tiene todo lo que necesitas para hacerlo. Consulta: + +1. [Opciones de solicitud (`parseAs`)](/adaptors/packages/http-docs#requestoptions) +2. [Codificar](/adaptors/packages/http-docs#util_encode) una cadena en formato + Base64. +3. [Decodificar](/adaptors/packages/http-docs#util_decode) una cadena codificada + en Base64 para devolverla a su formato original. + +## Compatibilidad nativa en los adaptors {#adaptor-native-support} + +Algunos adaptors (DHIS2, FHIR-4, Sunbird-RC) manejan binarios de forma integrada +en endpoints conocidos de imágenes o archivos. Cuando solicitas un archivo (una +imagen, un PDF, etc.), la respuesta se convierte automáticamente en una cadena +codificada en base64. + +## Trabajar con Buffers {#working-with-buffers} + +También puedes trabajar directamente con buffers en el código de un job de +OpenFn, con código como este: + +```js +fn(state => { + const encoded = Buffer.from(state.data.myBase64string, 'base64'); + return { ...state, encodedImage }; +}); +``` + +o bien: + +```js +fn(state => { + const decoded = state.data.myBuffer.toString('base64'); + return { ...state, decoded }; +}); +``` + +## Manipulación de imágenes con el adaptor `image-utils` {#image-manipulation-with-the-image-utils-adaptor} + +En los workflows que necesitan transformar una imagen, y no solo moverla, usa el +[adaptor `image-utils`](/adaptors/packages/image-utils-docs). Ofrece: + +```js +// resize an image to given `width`/`height` dimensions. +resize(state.data.buffer, { width: 1200, height: 1600 }); +// reduce image quality/file size until it meets a target `maxBytes`, down to a `minQuality` floor. +compress(state.data.buffer, { maxBytes: 700 * 1024, minQuality: 20 }); +// remove all EXIF metadata from an image. +stripMetadata($.data.photoBase64); +// write EXIF key-value pairs (e.g. `UserComment`) into a JPEG. +embedMetadata($.data.buffer, { UserComment: 'patient-id=42' }); +// read an image's dimensions, orientation, size, and EXIF data without modifying it. +metadata($.data.photoBase64); +``` + +Cada operación acepta una cadena Base64 o un Buffer y escribe su resultado en +`state.data` (normalmente como `buffer`; si necesitas una cadena, tienes +disponible `parseAs: 'base64'`). + +Consulta la +[documentación del adaptor `image-utils`](/adaptors/packages/image-utils-docs) +para ver todos los detalles sobre las opciones y los valores que devuelve cada +función. + +## Resumen {#summary} + +La mayoría de los casos de uso, como obtener una imagen de un sistema y subirla +a otro, deberían **_simplemente funcionar™️_**. En los workflows que necesitan +transformar la propia imagen (cambiar su tamaño, comprimirla, quitarle o +agregarle datos EXIF, o leer sus metadatos), usa el +[adaptor `image-utils`](/adaptors/packages/image-utils-docs) como se describe +más arriba. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/javascript.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/javascript.md new file mode 100644 index 000000000000..180811e2ea83 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/javascript.md @@ -0,0 +1,331 @@ +--- +title: Consejos de JavaScript +sidebar_label: Consejos de JavaScript +translation_source_hash: 7faca517b9178d0323cb188a0b80fdb54558b6ff +translation_review_status: machine +--- + +OpenFn admite todas las características modernas de JavaScript. + +Esta sección destaca algunas características y operadores útiles de JavaScript +que pueden ayudarte a escribir código más limpio. No pretende ser una guía +exhaustiva, sino una referencia a algunas buenas técnicas sobre los aspectos más +nuevos del lenguaje. + +### Usar la operación `fn(...)` del adaptor common {#using-the-fn-operation-from-the-common-adaptor} + +Te recomendamos usar la operación `fn(...)` para manipular el state y aplicar +JavaScript personalizado que transforme, manipule y limpie los datos antes de +enviarlos a las aplicaciones de destino. + +```js +fn(state => { + //call state to edit + //add your custom javascript here to manipulate state + return state; //always return state +}); +``` + +### Variables: var, let y const {#variables-var-vs-let-vs-const} + +JavaScript ofrece tres formas distintas de declarar variables, y esto puede +resultar un poco confuso. + +- `var` es una variable cuyo valor se puede reasignar. Puedes volver a declarar + una `var` varias veces. +- `let` es básicamente lo mismo que una var, pero no se puede volver a declarar + y sus reglas de alcance son un poco distintas. +- `const` se usa para variables cuyo valor no cambia. + +En realidad no importa mucho qué estilo uses (salvo, quizás, si intentas asignar +un valor a una `const`). + +De todos modos, la mayoría de los jobs de OpenFn se escriben con un estilo +bastante funcional, así que es posible que ni siquiera necesites declarar +variables. + +
+¿Qué es la programación funcional? + +La programación funcional es un estilo de programación cada vez más popular en +el JavaScript moderno. + +En términos generales, la idea es reducir al mínimo el uso de sentencias de +control de flujo (como `if/else` o `for`) y usar en su lugar cadenas de +funciones. En la programación funcional, los datos pasan por un pipeline hasta +obtener el resultado que queremos. ¿Te suena? + +```js +const items = [10, 109, 55]; + +// Imperative JS +const transformedItems = []; +for (const i of items) { + if (i < 100) { + transformedItems.push(i * 2); + } +} + +// Functional js +const transformedItems = items.filter(x => x > 100).map(x => x * 2); +``` + +La programación funcional suele ser más breve y concisa que la programación +imperativa habitual. Esto tiene su lado bueno y su lado malo, pero si estás +acostumbrado al estilo, suele ser muy legible y se traslada bien de un lenguaje +a otro. + +
+ +La mayor parte del JavaScript moderno e idiomático se escribe con `const` y +`let`. Esto puede hacer que tu código sea más legible e intencional, y las +reglas son bastante sencillas: + +- Usa `const` si no quieres que cambie el valor de una variable. +- Usa `let` si esperas que cambie el valor de una variable. + +Esto puede complicarse un poco con los objetos y los arrays. Podemos asignar un +objeto a una const y aun así cambiar las propiedades del objeto. Lo mismo pasa +con los arrays. Todo esto tiene que ver con los punteros y con cómo JavaScript +almacena las variables: la clave es que no estás asignando un valor nuevo a la +variable, sino modificando el _contenido_ de la variable. + +Mira estos ejemplos: + +```js +// Example 1: Objects +const data = {}; + +// We can mutate the object here +// The data variable is still referencing the same object +data.name = 'OpenFn'; + +data = { name: 'Lightning' }; // This throws a runtime error because we are re-assigning the variable! + +// Example 2: Arrays +const ids = [1, 2, 3]; + +// We can call functions on the ids array, which will mutate the array's contents +ids.push(4); +ids.pop(); + +// But we cannot re-assign the variable + +ids = [4, 5, 6]; // This throws a runtime error because we are re-assigning the variable! +``` + +### Encadenamiento opcional {#optional-chaining} + +JavaScript es un lenguaje sin tipos, lo cual es muy conveniente para los jobs de +OpenFn y suele facilitar las cosas. + +Sin embargo, un problema común es que, al escribir cadenas largas de +propiedades, se lanza una excepción si falta alguna propiedad. Y esto pasa todo +el tiempo al obtener datos de servidores remotos. + +El encadenamiento opcional permite que JavaScript deje de evaluar una cadena de +propiedades y devuelva undefined como resultado de toda la expresión: + +```js +const x = a.b?.c?.d?.e; +``` + +En este ejemplo, si `c`, por ejemplo, no está definida, `x` recibe el valor +`undefined`. No se lanza ninguna excepción. + +También puedes hacerlo con propiedades de tipo string, aunque la sintaxis es un +poco más engorrosa: + +```js +const x = a.b['missing-link']?.d?.e; +``` + +También sirve para llamadas opcionales a funciones (es menos útil al escribir +jobs, pero lo incluimos para que esté completo): + +```js +const x = a.b?.(); +``` + +Puedes combinar el encadenamiento opcional con el operador de **"fusión de +nulos"** (nullish coalescing), que tiene un nombre estupendo. Funciona un poco +como una expresión ternaria o como un or: si lo que está a la izquierda del +operador devuelve `null` o `undefined`, se devuelve el valor de la derecha. + +```js +const x = a.b?.c?.d?.e ?? 22; +``` + +En este ejemplo, si alguno de los valores de la cadena no está definido, `x` +recibe el valor 22. + +### Funciones flecha {#arrow-functions} + +Usamos funciones flecha en toda esta guía y suponemos que la mayoría de los +usuarios ya sabe usarlas. + +Una función flecha es otra forma de escribir una función de JavaScript. Hay +varias razones por las que son populares en el JavaScript moderno: + +- Resultan ligeras, porque requieren menos sintaxis +- No tienen un alcance `this`, aunque esto es en gran parte irrelevante para + programar en OpenFn (y, de hecho, para la mayoría de los frameworks modernos + de JS) + +Las funciones flecha son siempre anónimas (no tienen nombre), pero, por +supuesto, se pueden asignar a variables. + +```js +function upperCase(name) { + return name.toUpperCase(); +} + +const getName = () => { + return name.toUpperCase(); +}; +``` + +Una función flecha puede contener una sola expresión y ningún cuerpo, y en ese +caso devuelve esa expresión: + +```js +function getX() { + return x; +} + +const getX = () => x; +``` + +Este patrón hace que las funciones flecha sean ligeras y elegantes, y encaja muy +bien con los paradigmas de la programación funcional. + +:::tip ¿Problemas para devolver un objeto? + +Encierra siempre los objetos entre paréntesis cuando devuelvas un objeto desde +una función flecha: + +``` +post('wwww', () => ({ id: 'a', name: 'adam' })) +``` + +Cuando JavaScript ve una llave `{` después de una flecha, espera un bloque de +sentencias, no un objeto. Encerrar el objeto entre paréntesis le indica a +JavaScript que debe interpretar una expresión en lugar de un bloque. + +::: + +### Operadores rest y spread {#rest-and-spread-operators} + +El operador spread o rest `...` sirve para varias cosas. Puede ser bastante +difícil de entender, pero en OpenFn tiene un par de usos muy útiles. + +Primero, puedes **"esparcir"** (spread) o **"aplicar"** las propiedades y los +valores de uno o más objetos en un objeto nuevo. Es una forma muy práctica de +hacer una copia superficial de un objeto. + +Funciona de forma muy parecida a `Object.assign(obj, first, second, third)`. + +Así se hace una copia superficial con spread: + +```js +const newState = { + ...state, +}; +``` + +Las propiedades se declaran en orden, así que puedes esparcir un objeto y luego +declarar más propiedades: + +```js +const newState = { + ...state + data: {} // create a new data object but keep all other keys of state +} +``` + +Puedes esparcir varios objetos, que también se aplican en orden. Este ejemplo +aplica algunos valores predeterminados, luego los sobrescribe con lo que haya en +el state y, por último, sobrescribe la clave data. + +```js +const newState = { + ...defaults, + ...state + data: {} // create a new data object but keep all other keys of state +} +``` + +Esparcir de esta forma no afecta al objeto original (es decir, en el ejemplo +anterior, `defaults` y `state` no cambian). Pero recuerda que solo es una copia +superficial, y que los valores no primitivos usan punteros, no copias. + +
+¿Qué es una copia superficial? + +Hacer una copia superficial de un objeto significa copiar todas las claves y los +valores de primer nivel de ese objeto en un objeto nuevo. + +Pero esto SOLO se aplica a las claves de primer nivel. Y si un valor contiene un +objeto, en realidad solo estás copiando un _puntero_ a ese objeto. + +```js +const a = { + x: 1, + y: { + values: [1, 2, 3] + } +}; + +// declare b as a shallow clone of a +const b = { + ... a +} + +b.x = 2; // a.x is unchanged +b.y.values = []; // a.y.values is changed +b.y = 20' // a.y is unchanged +``` + +Una copia profunda significa que se copian todas las propiedades de todo el +árbol del objeto. + +
+ +### Implementar reglas de mapeo y variables globales {#implementing-mapping-rules-and-global-variables} + +Si tienes una lista global de variables o reglas de mapeo que quieres usar en +tus workflows, puedes agregarlas a tu job como una constante y hacer referencia +a ella tantas veces como quieras en la expresión del job. Consulta la +documentación sobre [especificaciones de mapeo](/design/mapping-specs.md) para +obtener más información sobre las variables globales. + +```js +//Workflow step 1 +//Global mapping rules you want to implement in your workflow +const locationMap = { + //location_id from source app: location value in destination app + 01: 'Western Cape', + 02: 'Eastern Cape', + 03: 'Gauteng' +} +//First we use fn() to transform, map & clean our data +fn(state => { + // Here we build the payload of our http request body... + // We assume the input is an array of records + const payload = state.data.map(record => ({ + location: locationMap[record.location_id] //translate location_id to the mapped value + external_id: record.case_id + })); + + return {...state, payload}; +}); + +//Workflow step 2 +//Then we post the payload built in the prior operation to create a record +post('/api/myEndpoint', { + headers: { + 'Content-Type': 'application/json', + }, + body: (state) => state.payload +}); +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/job-examples.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/job-examples.md new file mode 100644 index 000000000000..9a0755c17d63 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/job-examples.md @@ -0,0 +1,424 @@ +--- +title: Ejemplos de código de jobs +sidebar_label: Ejemplos de jobs +translation_source_hash: e6b804b61b644b95048adfce67db6c76a4519068 +translation_review_status: machine +--- + +Aquí encontrarás bloques de código para distintas funciones y formas de manejar +datos que puedes usar en tus jobs. + +:::tip + +Para ver jobs de ejemplo escritos por el equipo principal de OpenFn y otros +usuarios, consulta la [biblioteca](/adaptors/library) u otros repositorios de +proyectos en [GitHub.com/OpenFn](https://github.com/OpenFn). + +::: + +:::info ¿Preguntas? + +Si tienes preguntas sobre cómo escribir jobs, pregunta en la +[comunidad](https://community.openfn.org) para recibir ayuda del equipo +principal de OpenFn y de otros implementadores. + +::: + +### Expresión de job (de CommCare a SF) {#job-expression-for-commcare-to-sf} + +La siguiente expresión de job toma un recibo que coincide y usa sus datos para +hacer un upsert de un registro `Patient__c` en Salesforce y crear varios +registros nuevos de `Patient_Visit__c` (hijos de Patient). + +```js +upsert( + 'Patient__c', + 'Patient_Id__c', + fields( + field('Patient_Id__c', dataValue('form.patient_ID')), + relationship('Nurse__r', 'Nurse_ID_code__c', dataValue('form.staff_id')), + field('Phone_Number__c', dataValue('form.mobile_phone')) + ) +); +each( + join('$.data.form.visits[*]', '$.references[0].id', 'Id'), + create( + 'Visit__c', + fields( + field('Patient__c', dataValue('Id')), + field('Date__c', dataValue('date')), + field('Reason__c', dataValue('why_did_they_see_doctor')) + ) + ) +); +``` + +### Acceder al "data array" en los envíos de Open Data Kit {#accessing-the-data-array-in-open-data-kit-submissions} + +Fíjate en cómo usamos "each" para obtener los datos de cada elemento del "data +array" en ODK. + +```js +each( + '$.data.data[*]', + create( + 'ODK_Submission__c', + fields( + field('Site_School_ID_Number__c', dataValue('school')), + field('Date_Completed__c', dataValue('date')), + field('comments__c', dataValue('comments')), + field('ODK_Key__c', dataValue('*meta-instance-id*')) + ) + ) +); +``` + +### De ODK a Salesforce: crear un registro padre con muchos hijos a partir de los datos del padre {#odk-to-salesforce-create-parent-record-with-many-children-from-parent-data} + +Aquí, el usuario lleva `time_end` y `parentId` del objeto padre a cada línea de +detalle. + +```js +each( + dataPath('data[*]'), + combine( + create( + 'transaction__c', + fields( + field('Transaction_Date__c', dataValue('today')), + relationship( + 'Person_Responsible__r', + 'Staff_ID_Code__c', + dataValue('person_code') + ), + field('metainstanceid__c', dataValue('*meta-instance-id*')) + ) + ), + each( + merge( + dataPath('line_items[*]'), + fields( + field('end', dataValue('time_end')), + field('parentId', lastReferenceValue('id')) + ) + ), + create( + 'line_item__c', + fields( + field('transaction__c', dataValue('parentId')), + field('Barcode__c', dataValue('product_barcode')), + field('ODK_Form_Completed__c', dataValue('end')) + ) + ) + ) + ) +); +``` + +> **Nota: había un error conocido en la función `combine` que ya se corrigió. +> `combine` sirve para combinar dos operaciones en una y se suele usar para +> ejecutar varios `create` dentro de un `each(path, operation)`. Puedes ver el +> código fuente de combine aquí: +> [language-common: combine](https://github.com/OpenFn/language-common/blob/master/src/index.js#L204-L222)** + +### Crear muchos registros hijos SIN un grupo de repetición en ODK {#create-many-child-records-without-a-repeat-group-in-odk} + +```js +beta.each( + '$.data.data[*]', + upsert( + 'Outlet__c', + 'Outlet_Code__c', + fields( + field('Outlet_Code__c', dataValue('outlet_code')), + field('Location__Latitude__s', dataValue('gps:Latitude')), + field('Location__Longitude__s', dataValue('gps:Longitude')) + ) + ) +); +beta.each( + '$.data.data[*]', + upsert( + 'Outlet_Call__c', + 'Invoice_Number__c', + fields( + field('Invoice_Number__c', dataValue('invoice_number')), + relationship('Outlet__r', 'Outlet_Code__c', dataValue('outlet_code')), + relationship('RecordType', 'name', 'No Call Card'), + field('Trip__c', 'a0FN0000008jPue'), + relationship( + 'Sales_Person__r', + 'Sales_Rep_Code__c', + dataValue('sales_rep_code') + ), + field('Date__c', dataValue('date')), + field('Comments__c', dataValue('comments')) + ) + ) +); +``` + +### Salesforce: actualizar un registro {#salesforce-perform-an-update} + +```js +update("Patient__c", fields( + field("Id", dataValue("pathToSalesforceId")), + field("Name__c", dataValue("patient.first_name")), + field(...) +)); +``` + +### Salesforce: definir el tipo de registro con 'relationship(...)' {#salesforce-set-record-type-using-relationship} + +```js +create( + 'custom_obj__c', + fields( + relationship( + 'RecordType', + 'name', + dataValue('submission_type'), + field('name', dataValue('Name')) + ) + ) +); +``` + +### Salesforce: definir el tipo de registro con el ID del tipo de registro {#salesforce-set-record-type-using-record-type-id} + +```js +each( + '$.data.data[*]', + create( + 'fancy_object__c', + fields( + field('RecordTypeId', '012110000008s19'), + field('site_size', dataValue('size')) + ) + ) +); +``` + +### Telerivet: enviar un SMS a partir de una alerta de workflow de Salesforce {#telerivet-send-sms-based-on-salesforce-workflow-alert} + +```js +send( + fields( + field( + 'to_number', + dataValue( + 'Envelope.Body.notifications.Notification.sObject.phone_number__c' + ) + ), + field('message_type', 'sms'), + field('route_id', ''), + field('content', function (state) { + return 'Hey there. Your name is '.concat( + dataValue('Envelope.Body.notifications.Notification.sObject.name__c')( + state + ), + '.' + ); + }) + ) +); +``` + +### HTTP: hacer fetch sin fallar {#http-fetch-but-dont-fail} + +```js +// ============= +// We use "fetchWithErrors(...)" so that when the +// SMS gateway returns an error the run does not "fail". +// It "succeeds" and then delivers that error message +// back to Salesforce with the "Update SMS Status" job. +// ============= +fetchWithErrors({ + getEndpoint: 'send_to_contact', + query: function (state) { + return { + msisdn: + state.data.Envelope.Body.notifications.Notification.sObject + .SMS__Phone_Number__c, + message: + state.data.Envelope.Body.notifications.Notification.sObject + .SMS__Message__c, + api_key: 'some-secret-key', + }; + }, + externalId: state.data.Envelope.Body.notifications.Notification.sObject.Id, + postUrl: 'https://www.openfn.org/inbox/another-secret-key', +}); +``` + +### Ejemplo de job para la API de eventos de DHIS2 {#sample-dhis2-events-api-job} + +```js +event( + fields( + field('program', 'eBAyeGv0exc'), + field('orgUnit', 'DiszpKrYNg8'), + field('eventDate', dataValue('properties.date')), + field('status', 'COMPLETED'), + field('storedBy', 'admin'), + field('coordinate', { + latitude: '59.8', + longitude: '10.9', + }), + field('dataValues', function (state) { + return [ + { + dataElement: 'qrur9Dvnyt5', + value: dataValue('properties.prop_a')(state), + }, + { + dataElement: 'oZg33kd9taw', + value: dataValue('properties.prop_b')(state), + }, + { + dataElement: 'msodh3rEMJa', + value: dataValue('properties.prop_c')(state), + }, + ]; + }) + ) +); +``` + +### Ejemplo de job para la API de data value sets de DHIS2 {#sample-dhis2-data-value-sets-api-job} + +```js +dataValueSet( + fields( + field('dataSet', 'pBOMPrpg1QX'), + field('orgUnit', 'DiszpKrYNg8'), + field('period', '201401'), + field('completeData', dataValue('date')), + field('dataValues', function (state) { + return [ + { dataElement: 'f7n9E0hX8qk', value: dataValue('prop_a')(state) }, + { dataElement: 'Ix2HsbDMLea', value: dataValue('prop_b')(state) }, + { dataElement: 'eY5ehpbEsB7', value: dataValue('prop_c')(state) }, + ]; + }) + ) +); +``` + +### Ejemplo de expresión de OpenMRS que crea una persona y luego un paciente {#sample-openmrs-expression-creates-a-person-and-then-a-patient} + +```js +person( + fields( + field('gender', 'F'), + field('names', function (state) { + return [ + { + givenName: dataValue('form.first_name')(state), + familyName: dataValue('form.last_name')(state), + }, + ]; + }) + ) +); +patient( + fields( + field('person', lastReferenceValue('uuid')), + field('identifiers', function (state) { + return [ + { + identifier: '1234', + identifierType: '8d79403a-c2cc-11de-8d13-0010c6dffd0f', + location: '8d6c993e-c2cc-11de-8d13-0010c6dffd0f', + preferred: true, + }, + ]; + }) + ) +); +``` + +### Combinar muchos valores en una ruta hija {#merge-many-values-into-a-child-path} + +```js +each( + merge( + dataPath("CHILD_ARRAY[*]"), + fields( + field("metaId", dataValue("*meta-instance-id*")), + field("parentId", lastReferenceValue("id")) + ) + ), + create(...) +) +``` + +### arrayToString {#arraytostring} + +```js +arrayToString(arr, separator_string); +``` + +### Acceder a la URL de una imagen en un envío de ODK {#access-an-image-url-from-an-odk-submission} + +```js +// In ODK the image URL is inside an image object... +field("Photo_URL_text__c", dataValue("image.url")), +``` + +### alterState (alterar el state) para asegurarte de que los datos estén en un array {#alterstate-alter-state-to-make-sure-data-is-in-an-array} + +```js +// Here, we make sure CommCare gives us an array to use in each(merge(...), ...) +fn(state => { + const idCards = state.data.form.ID_cards_given_to_vendor; + if (!Array.isArray(idCards)) { + state.data.form.ID_cards_given_to_vendor = [idCards]; + } + return state; +}); + +// Now state has been changed, and we carry on... +each( + merge( + dataPath('form.ID_cards_given_to_vendor[*]'), + fields( + field('Vendor_Id', dataValue('form.ID_vendor')), + field('form_finished_time', dataValue('form.meta.timeEnd')) + ) + ), + upsert( + 'Small_Packet__c', + 'sp_id__c', + fields( + field('sp_id__c', dataValue('ID_cards_given_to_vendor')), + relationship('Vendor__r', 'Badge_Code__c', dataValue('Vendor_Id')), + field( + 'Small_Packet_Distribution_Date__c', + dataValue('form_finished_time') + ) + ) + ) +); +``` + +### Iniciar sesión en un servidor con un certificado SSL personalizado {#log-in-to-a-server-with-a-custom-ssl-certificate} + +Este fragmento muestra cómo conectarte a un servidor seguro sin verificar el +certificado SSL. Define `strictSSL: false` en el argumento de opciones de la +función `post` de `language-http`. + +```js +post( + `${state.configuration.url}/${path}`, + { + headers: { 'content-type': 'application/json' }, + body: { + email: 'Luka', + password: 'somethingSecret', + }, + strictSSL: false, + }, + callback +); +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/job-snippets.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/job-snippets.md new file mode 100644 index 000000000000..1f28fbe96ed0 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/job-snippets.md @@ -0,0 +1,131 @@ +--- +title: Fragmentos de código +sidebar_label: Fragmentos de código +translation_source_hash: 45bf7a84c28b09f0af8a976d6d3b790ebc094a9d +translation_review_status: machine +--- + +Esta sección reúne varios fragmentos de código JavaScript útiles que puedes usar +en tus jobs. + +La mayoría de los fragmentos están implementados como callbacks de otras +operaciones. + +Puedes copiar estos callbacks y adaptarlos a tu propio código. + +## General {#general} + +### Reemplazo personalizado {#custom-replacer} + +```js +field('destination__c', state => { + return dataValue('path_to_data')(state).toString().replace('cats', 'dogs'); +}); +``` + +Esto reemplaza todos los "cats" por "dogs" en la cadena que está en +`path_to_data`. + +> **NOTA:** La función `replace()` de JavaScript solo reemplaza la primera +> aparición del argumento que indiques. Si buscas una forma de reemplazar todas +> las apariciones, te sugerimos usar una regex como hicimos en el +> [ejemplo](#concatenation-of-null-values) de más abajo. + +### arrayToString personalizado {#custom-arraytostring} + +```js +field("target_specie_list__c", function(state) { + return Array.apply( + null, sourceValue("$.data.target_specie_list")(state) + ).join(', ') +}), +``` + +Toma un array y concatena cada elemento en una cadena, con ", " como separador. + +### Concatenación personalizada {#custom-concatenation} + +```js +field('ODK_Key__c', function (state) { + return dataValue('metaId')(state).concat('(', dataValue('index')(state), ')'); +}); +``` + +Esto concatena dos valores. + +### Concatenación de valores nulos {#concatenation-of-null-values} + +Esto concatena muchos valores, aunque uno o más sean nulos, y los escribe en un +campo llamado Main_Office_City_c. + +```js +... + field("Main_Office_City__c", function(state) { + return arrayToString([ + dataValue("Main_Office_City_a")(state) === null ? "" : dataValue("Main_Office_City_a")(state).toString().replace(/-/g, " "), + dataValue("Main_Office_City_b")(state) === null ? "" : dataValue("Main_Office_City_b")(state).toString().replace(/-/g, " "), + dataValue("Main_Office_City_c")(state) === null ? "" : dataValue("Main_Office_City_c")(state).toString().replace(/-/g, " "), + dataValue("Main_Office_City_d")(state) === null ? "" : dataValue("Main_Office_City_d")(state).toString().replace(/-/g, " "), + ].filter(Boolean), ',') + }) +``` + +> Fíjate en que esta función personalizada usa la **regex** `/-/g` para +> asegurarse de que se tengan en cuenta todas las apariciones (g = búsqueda +> global). + +### ID personalizado de la enésima referencia {#custom-nth-reference-id} + +Si alguna vez quieres obtener el PRIMER objeto que creaste, o el SEGUNDO, o el +enésimo, una función como esta te sirve. + +```js +field('parent__c', function (state) { + return state.references[state.references.length - 1].id; +}); +``` + +Fíjate en que, en lugar de tomar el id de lo "último" que se creó en Salesforce, +tomas el id de lo primero, o de lo segundo si reemplazas "length-1" por +"length-2". + +## Salesforce {#salesforce} + +### Convertir una cadena de fecha al formato ISO estándar para Salesforce {#convert-date-string-to-standard-iso-date-for-salesforce} + +```js +field('Payment_Date__c', function (state) { + return new Date(dataValue('payment_date')(state)).toISOString(); +}); +``` + +> **NOTA**: La salida de esta función siempre tiene el formato de la zona +> horaria GMT. + +### Usar campos de ID externo para las relaciones en una carga masiva en Salesforce {#use-external-id-fields-for-relationships-during-a-bulk-load-in-salesforce} + +```js +array.map(item => { + return { + Patient_Name__c: item.fullName, + 'Account.Account_External_ID__c': item.account + 'Clinic__r.Unique_Clinic_Identifier__c': item.clinicId, + 'RecordType.Name': item.type, + }; +}); +``` + +### Upsert masivo con un ID externo en Salesforce {#bulk-upsert-with-an-external-id-in-salesforce} + +```js +bulk( + 'Visit_new__c', + 'upsert', + { + extIdField: 'commcare_case_id__c', + failOnError: true, + allowNoOp: true, + }, + dataValue('patients') +); +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/job-writing-guide.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/job-writing-guide.md new file mode 100644 index 000000000000..6187c00f9d54 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/job-writing-guide.md @@ -0,0 +1,75 @@ +--- +sidebar_label: Introducción +title: Guía para escribir jobs +translation_source_hash: 9d3ec7d47868cbee41ed5f200085af202ed96bfe +translation_review_status: machine +--- + +En OpenFn, la automatización de workflows y la integración de datos se logran +mediante la creación de jobs. + +Esta guía te presenta los conceptos clave y las buenas prácticas para escribir +jobs. Sirve tanto para quienes recién empiezan a programar como para +programadores de JavaScript con experiencia. De hecho, incluso si ya tienes +experiencia con JavaScript, hay varios patrones clave del ecosistema de OpenFn +que es importante aprender. + +:::tip + +Si escribes jobs en la app de la plataforma (Lightning), puedes usar el +[AI Assistant](/build/ai-assistant.md) como ayuda. Lo encontrarás en el +Inspector. + +::: + +Un job es un conjunto de código JavaScript que realiza una tarea concreta, como +obtener datos de Salesforce o convertir datos JSON al estándar FHIR. + +Cada job usa exactamente un adaptor (a menudo llamado "conector") para realizar +su tarea. El adaptor ofrece un conjunto de funciones auxiliares (operaciones) +que facilitan la comunicación con una fuente de datos. + +Esta guía sirve por igual para escribir jobs en la app (Lightning) o con la CLI. + +:::info Workflows + +Puedes encadenar varios jobs en un workflow. Un patrón común es usar un job para +obtener datos de la fuente de datos A, otro job para convertir o transformar +esos datos para que sean compatibles con la fuente de datos B, y un tercer job +para subir los datos transformados a la fuente de datos B. + +Para saber más sobre el diseño y la implementación de workflows, consulta +[Crear y gestionar workflows](/build/workflows.md). + +::: + +## Próximos pasos {#next-steps} + +La mejor forma de aprender a escribir jobs de OpenFn es escribir jobs de OpenFn. + +Puedes [empezar con la CLI](/build-for-developers/cli-intro.md) y ejecutar jobs +en tu computadora. Después, mira el +[desafío de la CLI](/build-for-developers/cli-challenges.md) para poner a prueba +de verdad tus habilidades para escribir jobs. + +Si ya quieres empezar a usar la app, mira esta guía para +[crear tu primer workflow](/build/workflows.md). + +El diseño de workflows no es un problema trivial, así que quizás también quieras +revisar la [documentación del proceso de diseño](/design/design-overview.md) de +workflows. + +A medida que tus jobs crecen, empezarás a escribir funciones auxiliares dentro +de ellos para analizar, mapear o reformatear datos. Puedes hacer pruebas +unitarias de esas funciones como con cualquier otro código JavaScript: +expórtalas en el nivel superior, compila tu proyecto con la CLI y apunta un +ejecutor de pruebas a la salida. Consulta +[Escribir pruebas unitarias para tus jobs](/documentation/jobs/unit-testing-jobs). + +:::info ¿Preguntas? + +Si tienes preguntas sobre cómo escribir jobs, pregunta en la +[comunidad](https://community.openfn.org) para recibir ayuda del equipo +principal de OpenFn y de otros implementadores. + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/lazy-state-operator.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/lazy-state-operator.md new file mode 100644 index 000000000000..5e50e5ae93cb --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/lazy-state-operator.md @@ -0,0 +1,181 @@ +--- +sidebar_label: Operador lazy state +title: El operador lazy state +translation_source_hash: 13268eb8864e380f9070d38359a4cb3ab44acf65 +translation_review_status: machine +--- + +## El operador lazy state {#the-lazy-state-operator} + +:::tip Funcionalidad experimental + +El operador lazy state llegó a OpenFn en abril de 2024. Todavía se considera una +funcionalidad experimental, pero funciona muy bien, ¡y te animamos a usarlo! + +Si tienes comentarios, problemas o sugerencias sobre el operador lazy state, +¡nos encantaría saber de ti en la [comunidad](https://community.openfn.org)! +También puedes abrir un issue en [GitHub](https://github.com/openfn/kit/issues). + +::: + +El operador lazy state es una sintaxis abreviada que facilita leer el state +cuando pasas datos a una operación. + +En lugar de escribir `state.data` para acceder a algo del state, puedes usar +`$`, así: + +```js +get($.data.url); +``` + +El `$` garantiza que el valor que se pasa a la operación se resuelva en el +momento correcto. Piensa en ello como pasar una ruta a una parte del state, en +lugar de pasar el valor de esa ruta. + +Lo bueno es que básicamente puedes ignorar por completo la sección anterior y no +pensar demasiado en cuándo se evalúa el state. Solo lee de `$` como si fuera tu +objeto state, y el runtime de OpenFn resolverá el valor correctamente en el +momento de la ejecución. + +El símbolo `$` es en realidad solo azúcar sintáctico para `(state) => state` (en +la mayoría de los casos, solo hacemos un reemplazo de texto al compilar tu +código). Estas dos sentencias se comportan exactamente igual: + +```js +get($.data.url); +get((state) => state.data.url); +``` + +Lo llamamos "lazy state" (state diferido) porque el motor del runtime resuelve +la referencia justo antes de usarla. Así se evitan muchos de los problemas de +asincronía de JavaScript que se explican en +[Leer el state de forma diferida](/jobs/operations.md#reading-state-lazily). + +:::tip $ solo funciona dentro de las operaciones + +`$` solo funciona dentro de una expresión que se pasa a una operación. Dicho de +otro modo, solo puedes usarlo donde podrías escribir `(state) => state` en su +lugar (como en el ejemplo anterior). + +::: + +## Ejemplos de uso {#usage-examples} + +Los siguientes fragmentos de código muestran algunas formas de usar el operador +lazy state. Cada ejemplo se puede reescribir sin `$`, pero con él la sintaxis es +más corta, más legible y más expresiva. + +El uso básico es simplemente pasar el state a una operación: + +```js +upsert('patient', $.data.patients[0]); +``` + +Puedes usarlo dentro de un objeto (siempre que ese objeto se pase a una +operación): + +```js +create('agent', { + name: $.patient.name, + country: $.patient.country, +}); +``` + +Puedes usarlo dentro de una plantilla de string: + +```js +get(`/patients/${$.patient.id}`); +``` + +O dentro de otras expresiones, como una concatenación: + +```js +create({ + name: $.patients[0].first_name + ' ' + $.patients[0].last_name, +}); +``` + +O en operaciones matemáticas: + +```js +create({ + profit: $.report.revenue - $.report.expenses, +}); +``` + +Puedes usarlo al mapear estructuras de datos: + +```js +create('user', { + countryCode: countries[$.location.country], +}); +``` + +Y puedes usarlo en operaciones anidadas, como con `each()`: + +```js +each($.data.patients, + post(`patients/${$.data.patient.id}`, $.data.patient) +); +``` + +## $ no es state {#-is-not-state} + +El operador `$` **no** es un alias de `state`. + +No se puede usar en lugar de la variable `state`. No se le puede asignar un +valor ni puede estar en el lado izquierdo de una asignación, y solo se puede +usar dentro de un argumento de una función. + +Esto también significa que el operador lazy state solo sirve para LEER el state. +No se puede usar para asignar valores directamente al state. + +Todos estos ejemplos son errores: + +```js +❌ const url = $.data.url; +get(url); + +❌ get(() => $.data.url); + +❌ $.data.x = fn(); + +❌ fn(state => { + $.data.x = 10; +}); +``` + +
+Reglas de compilación para usuarios avanzados + +¿Cómo funciona el operador lazy state? La "magia" está en el compilador. + +En pocas palabras, cada vez que el compilador ve `$` en tu código, lo reemplaza +por `(state) => state`. Así: + +``` +get($.data.url) // compiles to get((state) => state.data.url) +``` + +En la práctica, las reglas son un poco más complicadas. Cuando ve un operador +`$`, el compilador primero comprueba que `$` no se haya declarado como variable +o parámetro. Si se declaró, lo ignora por completo. + +Pero si considera que `$` es un operador de state, el compilador primero +reemplaza el símbolo `$` por `state`, luego busca la operación a la que se está +llamando y, por último, envuelve el argumento en una función flecha (si todavía +no lo está). + +``` +get({ url: $.data.url }) // compiles to get((state) => { url: state.data.url }) +``` + +Este "hoisting" de la función flecha permite usar expresiones más complejas e +interesantes con lazy state, como plantillas de string o búsquedas dinámicas en +objetos. + +Si tienes curiosidad (o necesitas resolver algún problema), puedes usar el +comando `openfn compile` de la CLI para ver el código compilado, que te mostrará +cómo trata el compilador tus operadores de state. + +
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/operations.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/operations.md new file mode 100644 index 000000000000..d19134ebcca9 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/operations.md @@ -0,0 +1,405 @@ +--- +sidebar_label: Operaciones +title: Operaciones +translation_source_hash: 33c7730014de711a9d329f1afb74aa6c7312b32b +translation_review_status: machine +--- + +Las operaciones son funciones de JavaScript que expone un adaptor y que se usan +en el código del job para _hacer cosas_. + +Las operaciones las proporciona un adaptor (conector). Cada adaptor exporta una +lista de funciones pensadas para interactuar con una fuente de datos en +particular. Por ejemplo, mira los adaptors de +[dhis2](/adaptors/packages/dhis2-docs) y +[salesforce](/adaptors/packages/salesforce-docs). + +Todo lo que puedes lograr en OpenFn también se puede lograr con bibliotecas de +JavaScript existentes o con llamadas a APIs REST. El valor de los adaptors está +en que ofrecen funciones que lo hacen más fácil: se encargan de la autorización, +ofrecen una sintaxis más limpia y te ocultan los detalles de implementación. + +Por ejemplo, así de simple es hacer una solicitud GET con el adaptor http: + +```js +get('/patients'); +``` + +El primer argumento de `get` es la ruta a la que se hace la solicitud (la +configuración le indica al adaptor qué URL base usar). En este caso pasamos una +cadena fija, pero también podemos pasar un valor del state: + +```js +get(state => state.endpoint); +``` + +
+¿Por qué la función flecha? + +Si tienes algo de experiencia con JavaScript, notarás que el ejemplo anterior +usa una función flecha para obtener la clave endpoint del state. + +Pero ¿por qué no hacer simplemente esto? + +``` +get(state.endpoint); +``` + +El problema es que el valor del state debe resolverse de forma diferida (es +decir, justo antes de que el get se ejecute). Por cómo funciona JavaScript, si +escribimos el valor directamente, podría leerse antes de que se haya asignado +state.endpoint. + +Para más detalles, salta a +[Leer el state de forma diferida](#reading-state-lazily). + +
+ +El código de tu job solo debería contener operaciones en el nivel superior +(alcance superior). NO deberías incluir ninguna otra instrucción de JavaScript. +Hablaremos más de esto en un momento. + +## Las operaciones se ejecutan en el nivel superior {#operations-run-at-the-top-level} + +Las operaciones solo funcionan cuando están en el nivel superior del código de +tu job: + +```js +get('/patients'); +each('$.data.patients[*]', state => { + item.id = `item-${index}`; + return state; +}); +post('/patients', dataValue('patients')); +``` + +OpenFn llama a tus operaciones en serie durante la ejecución del workflow y se +asegura de que cada una reciba el state correcto. + +Si intentas anidar una operación dentro del callback de otra operación, fallará: + +```js +get('/patients', { headers: { 'content-type': 'application/json' } }, state => { + // This will fail because it is nested in a callback + each('$.data.patients[*]', (item, index) => { + item.id = `item-${index}`; + }); +}); +post('/patients', dataValue('patients')); +``` + +Esto se debe a que una operación es una función "fábrica": cuando se ejecuta, +devuelve una nueva función que debe invocarse con el state. El runtime de OpenFn +solo maneja esto correctamente en el alcance superior. La buena práctica es +construir cada operación del pipeline en el nivel superior y dejar que el state +pase de una a otra de forma natural. + +Si alguna vez de verdad necesitas una operación anidada, puedes invocarla de +inmediato y pasarle el state directamente, pero esto es un antipatrón y deberías +evitarlo: + +```js +get('/patients', { headers: { 'content-type': 'application/json' } }, state => { + each('$.data.patients[*]', (item, index) => { + item.id = `item-${index}`; + })(state); // anti-pattern: immediately invoke and pass state +}); +post('/patients', dataValue('patients')); +``` + +## Leer el state de forma diferida {#reading-state-lazily} + +Un problema común al escribir jobs es obtener el valor correcto del state en el +momento correcto. Mira este código: + +```js +get('/some-data'); +post('/some-other-data', state.data); +``` + +El `state.data` de la llamada a `post` se resolverá como `undefined` y el post +fallará. Esto se debe a que las operaciones son funciones fábrica: sus +parámetros se resuelven cuando se carga el módulo (antes de que se haya +ejecutado cualquier operación), así que `state.data` todavía no tiene un valor +asignado cuando `post` lo lee. + +La solución es pasar una función en lugar de un valor, para que la evaluación se +posponga hasta que la operación se ejecute de verdad: + +```js +get('/some-data'); +post('/some-other-data', state => state.data); +``` + +Cuando `post` se ejecuta, resuelve cualquier argumento que sea una función +llamándola con el state actual. Este patrón de evaluación diferida es +fundamental para escribir jobs de OpenFn correctos. Consulta también el +[operador lazy state](/jobs/lazy-state-operator.md) para una sintaxis abreviada. + +## Callbacks y fn() {#callbacks-and-fn} + +:::caution + +A partir de julio de 2024, los callbacks se irán eliminando de las APIs de los +adaptors. Consulta [Operaciones y promesas](#operations-and-promises) para ver +consejos sobre cómo usar callbacks con APIs de adaptors que no los admiten de +forma explícita. + +::: + +Muchas operaciones te dan acceso a una función callback. + +Los callbacks se invocan con el state, ejecutan el código que quieras y deben +devolver el siguiente state. Por lo general, tu callback se invoca como el +último paso de una operación. + +Esto es útil para interceptar y manipular el valor que devuelve una operación. + +
+¿Qué es un callback? + +Un callback es un patrón común en JavaScript. + +Es algo difícil de entender en abstracto: un callback es una función que le +pasas a otra función para que esta la invoque en un momento determinado. + +Se explica mejor con un ejemplo. Todos los arrays de JavaScript tienen una +función llamada `map`, que recibe un único argumento: un callback. + +Array.map recorre cada elemento del array, invoca tu función callback con él, +guarda el resultado en un array nuevo y, cuando termina, devuelve ese array. + +```js +const array = ['a', 'b', 'c']; +const result = array.map(item => { + return item.toUpperCase(); +}); +console.log(array); // ['a', 'b', 'c']; +console.log(result); // ['A', 'B', 'C']; +``` + +Como en JavaScript las funciones son datos, podemos reescribir ese código así +(quizás quede un poco más legible): + +```js +const array = ['a', 'b', 'c']; +const upperCase = item => { + return item.toUpperCase(); +}; +const result = array.map(upperCase); +console.log(array); // ['a', 'b', 'c']; +console.log(result); // ['A', 'B', 'C']; +``` + +
+ +La función `fn()`, por ejemplo, SOLO te permite definir un callback. Esto es +útil para ejecutar código arbitrario: si quieres pasar a JavaScript puro, así es +como se hace: + +```js +fn(state => { + // declare a helper function + const convertToFhir = item => { + /* ... */ + }; + + // Map data into a new format with native Javascript functions + state.transformed = state.data.map(convertToFhir); + + // Always return the state + return state; +}); +``` + +Muchas otras operaciones aceptan un argumento callback. En ese caso, tu callback +se invoca con el state y debe devolver el state final como resultado de la +operación. + +Por ejemplo, imagina que obtienes datos de una fuente de datos y recibes un +bloque de JSON. Quizás quieras filtrar esos datos antes de pasarlos a la +siguiente operación. + +Podrías intentar, de forma ingenua, algo como esto, ¡pero no va a funcionar! + +```js +get('/data'); // writes to state.data +state.data = state.data.filter(/* ... */); // This is invalid! +``` + +Podrías usar otra operación, como `fn` o `each`, y a menudo funcionan muy bien: + +```js +get('/data'); +fn(state => { + state.data = state.data.filter(/* ... */); + return state; +}); +``` + +Pero también puedes usar una función callback, que suele quedar un poco más +ordenada: + +```js +get('/data', {}, state => { + state.data = state.data.filter(/* ... */); + return state; +}); +``` + +Lo que devuelva tu callback se usará como state de entrada de la siguiente +operación (o será el state final del job). Así que recuerda devolver SIEMPRE el +state. + +Ten en cuenta que algunos adaptors escriben información interna en el state. Por +eso, por lo general deberías usar `return { ... state }` en lugar de +`return { data: state.data }`. + +:::tip + +¡Recuerda! Devuelve siempre el state desde un callback. + +::: + +## Operaciones y promesas {#operations-and-promises} + +:::tip + +El soporte para promesas se agregó en julio de 2024 en `@openfn/compiler@0.2.0`. +Está disponible en la CLI a partir de la versión 1.7.0 y en el worker de +Lightning a partir de la versión 1.4.0. + +::: + +Las operaciones se comportan como las promesas de JavaScript, ya que tienen las +funciones `.then()` y `.catch()`. Esto es útil para crear tus propios callbacks +y manejar errores. + +:::info Nota para desarrolladores + +El compilador es el que agrega el soporte para .then(). Técnicamente, las +operaciones no devuelven una promesa, sino una función, pero el compilador +modifica el código del job y envuelve la operación en una llamada a una promesa +diferida. + +::: + +### Callback con then() {#callback-with-then} + +Puedes encadenar `then()` en cualquier operación. Recibe un callback que se +ejecuta cuando la operación termina. + +El callback recibe el state que devuelve la operación y debe devolver el objeto +state que se pasará a la _siguiente_ operación. + +Por ejemplo: + +```js +get($.data.url).then(state => { + console.log(state.data); + return state; // always remember to return state! +}); +``` + +Si conoces el patrón de callbacks de nuestros adaptors, `.then()` cumple +exactamente la misma función que un callback. Te da la oportunidad de +transformar el state que devuelve una operación. + +Por lo general no necesitas un callback ni un `.then()`: puedes ejecutar las +operaciones en serie. El siguiente código es funcionalmente igual al ejemplo +anterior: + +```js +get($.data.url); +fn(state => { + console.log(state.data); + return state; // always remember to return state! +}); +``` + +`.then()` resulta especialmente útil al combinar operaciones con _state con +alcance_, como con `each()`: + +```js +each($.items, post(`patient/${$.data.id}`, $.data)); +``` + +:::tip + +Puedes leer más sobre la operación `each()` en +[Iteración con each](/jobs/data-transformation.md#iteration-with-each). + +::: + +La función `each` recibe un array y, por cada elemento, invoca un callback con +un state con alcance. Es decir, toma tu objeto state y asigna el elemento de la +iteración a `state.data`. Dicho de otro modo, dentro del callback, `state.data` +tiene como _alcance_ cada elemento del array. + +```js +each($.items, state => { + console.log(state.data); // each item in the items array + console.log(state.index); // the current index of iteration + return state; +}); +``` + +Así, en el ejemplo anterior, cada elemento de `state.items` se pasará a una +función HTTP `post()`, que incluirá el id en una URL y subirá el elemento al +servidor. + +¿Y si quieres hacer algo con el state con alcance DESPUÉS de la solicitud? +Quizás quieras revisar el código de estado y registrar un error, o modificar los +datos antes de volver a escribirlos en el state. + +Para esto puedes encadenar `operation().then()`: + +```js +each( + $.items, + post(`patient/${$.data.id}`, $.data).then(state => { + state.completed.push(state.data); + return state; + }) +); +``` + +Ahora esta expresión: + +- Recorre cada elemento de `state.items` +- Llama a la operación post con el state con alcance (es decir, el elemento en + `state.data`) +- Cuando el post termina, pasa el resultado como state con alcance al callback + de `.then()` + +### Manejo de errores con catch() {#error-handling-with-catch} + +La mayoría de los adaptors lanzan un error cuando algo sale mal, lo que puede +hacer que el job (y quizás incluso el workflow) termine antes de tiempo. + +Como cada operación tiene un `catch()`, puedes interceptar el error en el código +de tu job e incluso suprimirlo. + +```js +get('patients').catch((error, state) => { + state.error = error; + return state; +}); +``` + +El callback de error recibe dos argumentos: el error que lanzó el adaptor y el +objeto state. + +Si quieres que la ejecución continúe, deberías devolver el objeto state desde el +catch. Ese state se pasará luego a la siguiente operación. + +Si _sí_ quieres detener la ejecución, quizás con algún registro para depurar o +con un error diferente, deberías lanzar el error desde dentro del manejador +catch. + +```js +get('patients').catch((error, state) => { + console.log('Error ocurred faithing patients', error); + throw error; +}); +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/state.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/state.md new file mode 100644 index 000000000000..be67798cbc3d --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/state.md @@ -0,0 +1,229 @@ +--- +title: State de entrada y de salida +translation_source_hash: 5ac34f5e0595bb2aec97356e6635bfb55b9cff4c +translation_review_status: machine +--- + +Cada job necesita un state de entrada y, en la mayoría de los casos, produce un +state de salida. En este artículo se explican estos conceptos con más detalle. + +Todo job es un pipeline de transformación de datos. Recibe una entrada (un +objeto de JavaScript al que llamamos state) y ejecuta una serie de operaciones +(o funciones) que transforman ese state una tras otra. El objeto state final se +devuelve como la salida del pipeline. + +![Pipeline de un job](/img/guide-job-pipeline.webp) + +El state final de un job siempre debe ser un objeto de JavaScript serializable +(es decir, un objeto JSON). Se eliminan todas las claves que no se puedan +serializar. + +![Resumen del state de un job](/img/state-javascript.webp) + +:::tip Una nota sobre la terminología + +Al state de entrada también se le suele llamar _state inicial_, y al state de +salida, _state final_. Puedes usar estos términos indistintamente. + +::: + +## Claves del state {#state-keys} + +Los objetos state suelen tener las siguientes claves: + +- `data`: un almacén temporal de información, que se suele usar para guardar el + resultado de una operación concreta +- `configuration`: un objeto que contiene los datos de la credencial +- `references`: un historial de los valores anteriores de `data` +- `response`: los adaptors (como http) lo usan a menudo para guardar la + respuesta http sin procesar de una solicitud +- `errors`: una lista de los errores generados por un workflow concreto, + indexados por el nombre del job. + +Al final de un job, se elimina la clave configuration, junto con cualquier otra +clave que no se pueda serializar. + +A veces los adaptors escriben información adicional en el state durante un run. +Por ejemplo, los adaptors de bases de datos suelen escribir una clave `client` +en el state para hacer seguimiento de la conexión a la base de datos. Estas +claves se eliminan al final de un job. + +## State de entrada y de salida de los runs {#input--output-state-for-runs} + +El state de entrada de un run se genera de forma distinta según si ejecutas los +workflows localmente con la CLI o en la app: + +- Cuando creas una work order manualmente, tienes que seleccionar o generar la + entrada a mano (por ejemplo, creando un `Input` personalizado en la app, o un + archivo `state.json` si trabajas localmente + [en la CLI](/build-for-developers/cli-intro.md)). +- Cuando una work order se crea automáticamente mediante un trigger webhook o un + trigger cron, el state se crea como se describe más abajo. + +El state final de un run depende de lo que devuelva la última operación. +Recuerda que las expresiones de un job son una serie de operaciones: cada una +recibe el state y devuelve el state, después de generar cualquier número de +efectos secundarios. El state que se devuelve al final determina la salida del +run cuando terminan todas estas operaciones. + +Una buena práctica es incluir un último paso de limpieza del state que elimine +los datos que no deberían conservarse entre runs ni formar parte de la salida +(como información de identificación personal o PII), por ejemplo: + +```js +// get data from a data source +get('https://jsonplaceholder.typicode.com/users'); + +// store retrieved data in state for use later in job +fn(state => { + state.users = state.data; + return state; +}); + +// get more data from another data source +get('https://jsonplaceholder.typicode.com/posts'); + +// store additional retrieved data in state for use later in job +fn(state => { + state.posts = state.data; + return state; +}); + +// compare data +fn(state => { + if (state.users.length > state.posts.length) { + // do something based on the comparison + } + return state; +}); + +// cleanup state at the end before finishing job +fn(state => { + state.data = null; + state.users = null; + state.posts = null; + + return state; +}); +``` + +Hay algunos patrones comunes para limpiar el state final. Puedes devolver solo +las claves que necesitas: + +```js +fn(state => { + return { + data: state.data, + }; +}); +``` + +Usa el operador de propagación (spread) para conservarlo todo excepto algunas +claves que quieres sobrescribir: + +```js +fn(state => { + return { + ...state, + secretStuff: null, + }; +}); +``` + +O usa el operador _rest_ para excluir por completo algunas claves: + +```js +fn(state => { + const { username, password, secrets, ...rest } = state; + return rest; +}); +``` + +### Runs iniciados por un webhook {#webhook-triggered-runs} + +En la plataforma, cuando un evento de webhook inicia un run, el state de entrada +contiene las partes importantes de la **solicitud http** entrante. + +El state de entrada se verá más o menos así: + +```js +{ + data: { // the body of the http request + formId: "patient_enrollment", + name: "John Doe" + }, + request: { + method: "POST", + path: ['i', 'your-webhook-url-uuid'] // an ordered array with optional additional paths + headers: { "content-type": "application/json" }, // an object containing the headers of the request + query_params: {} // an object containing any query parameters + }, +} +``` + +### Runs iniciados por un cron {#cron-triggered-runs} + +Cuando un cron inicia un run, su state de entrada es el state final del run +anterior. Así, cada run sabe lo que pasó en los runs anteriores. Dicho de otro +modo, puedes pasar información de un run a otro aunque ocurran con días de +diferencia. + +**Escenario de ejemplo**: tienes una **sincronización diaria a las 9 AM** con un +workflow de 3 steps: (1) obtener los registros de pacientes, (2) transformar los +datos y (3) enviarlos a la base de datos. El lunes, el workflow procesa los +registros hasta el ID 1000 y devuelve `{ lastProcessedId: 1000 }` como state +final. El martes a las 9 AM, el cron vuelve a empezar con +`{ lastProcessedId: 1000 }` como entrada, así que sabe que tiene que obtener y +procesar los registros a partir del ID 1001. + +La primera vez que se ejecuta el workflow, el state inicial es simplemente un +objeto de JavaScript vacío: `{}` + +#### Sobrescribir la entrada del cron {#overriding-cron-input} + +Siempre puedes ejecutar manualmente un workflow con trigger cron con: + +- **Entrada vacía** (`{}`): empieza de cero, sin el state anterior. +- **Entrada personalizada**: tus propios datos, para probar escenarios + concretos. +- **Entrada predeterminada**: usa la misma entrada que los runs programados. + +Si el run manual tiene éxito, el siguiente run programado del cron empezará con +el state de salida que haya producido tu run manual. + +## State de entrada y de salida de los steps {#input--output-state-for-steps} + +El state también pasa de un step a otro dentro de un workflow. El state de +salida del step anterior se usa como state de entrada del step siguiente. + +### Si tiene éxito {#on-success} + +Cuando un job tiene éxito, su state de salida es lo que devuelva la última +operación. + +```js +{ + data: { patients: [] }, + references: [1, 2, 3] +} +``` + +### Si falla {#on-failure} + +Cuando falla un step de un workflow, el error se agrega a un objeto `errors` en +el state, con el ID del job que falló como clave. + +```js +{ + data: { patients: [] }, + references: [1, 2, 3], + errors: { + jobId: { /* error details */ } + } +} +``` + +En el siguiente diagrama puedes ver cómo podría pasar el state entre los steps +de un workflow. + +![Paso del state entre steps](/img/passing-state-steps.webp) diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/unit-testing-jobs.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/unit-testing-jobs.md new file mode 100644 index 000000000000..2344c0399f23 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/unit-testing-jobs.md @@ -0,0 +1,226 @@ +--- +sidebar_label: Pruebas unitarias de jobs +title: Escribir pruebas unitarias para tus jobs +translation_source_hash: 10c76a4fa251a5d9afb0b0df0d7b2dfe65f8ef6a +translation_review_status: machine +--- + +import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; + +La mayor parte del código de un job sigue el mismo patrón: obtiene algunos +registros, les cambia la forma y los envía a otro lugar. Pero esa parte de +transformación suele crecer hasta convertirse en lógica compleja: convertir un +texto en un registro estructurado, mapear códigos locales a elementos de datos +de DHIS2 o unificar una docena de formatos de fecha en uno solo. + +Hacer pruebas unitarias de esa lógica ayuda a comprobar que el código funciona +correctamente y a evitar errores cuando se modifique más adelante. + +:::info Requisitos + +Necesitas `@openfn/cli` v1.39.0 o posterior para compilar el código de tus jobs +para las pruebas. Comprueba tu versión con `openfn -v` y actualízala con +`npm install -g @openfn/cli`. + +También necesitas un proyecto de OpenFn descargado en tu computadora: + +```bash +openfn project pull +``` + +Así obtienes una carpeta con `openfn.yaml`, un directorio `workflows/` y un +archivo `.js` por cada step. Consulta [OpenFn Sync](/documentation/sync) para +saber cómo descargar proyectos, hacer checkout y desplegarlos. + +::: + +## Paso 1: exporta las funciones auxiliares que quieras probar {#step-1-export-any-helper-you-want-to-test} + +Las funciones que quieras probar tienen que estar exportadas: + +```js title="testable code" +export const FIELDS = ['id', 'name', 'dob']; + +export const parseSms = text => + Object.fromEntries(FIELDS.map((f, i) => [f, text.split('#')[i]])); + +fn(state => ({ ...state, data: state.data.messages.map(parseSms) })); +``` + +## Paso 2: compila tus workflows {#step-2-compile-your-workflows} + +Desde la raíz de tu proyecto (la carpeta que tiene `openfn.yaml`): + +```bash +openfn compile --exports-only +``` + +``` +[CLI] ✔ Compiled 1 step(s) to /path/to/project/dist +``` + +Los archivos compilados se guardan en `dist/`, con la misma estructura que tus +workflows. **Los archivos de salida usan la extensión `.mjs`.** Node siempre +trata los archivos `.mjs` como módulos ES, así que no necesitas +`"type": "module"` en tu `package.json` para que el código compilado se importe +sin problemas. + +:::warning No hagas commit de los archivos `.mjs` generados + +La CLI no agrega un `.gitignore` para el directorio compilado, así que agrégalo +tú antes de tu primer commit: + +```title=".gitignore" +dist/ +``` + +Los archivos `.mjs` son resultado de la compilación y salen por completo de tus +steps `.js`. Si los incluyes en el repositorio, cada edición te deja diffs +ruidosos y conflictos de merge, y `dist/` puede dejar de coincidir con +`workflows/`. + +::: + +Otras opciones útiles: + +```bash +# Write somewhere other than dist/ +openfn compile --exports-only -o workflows + +# Wipe the output folder first +openfn compile --exports-only --clean + +# Just one workflow, by name +openfn compile sms-parser --exports-only +``` + +Ejecuta `openfn compile --help` para ver la lista completa. + +También puedes definir la carpeta de salida de forma permanente en +`openfn.yaml`: + +```yaml title="openfn.yaml" +dirs: + workflows: workflows + compiled: workflows +``` + +## Paso 3: escribe una prueba {#step-3-write-a-test} + +Aquí recomendamos el ejecutor de pruebas integrado de Node porque no necesita +dependencias, pero nada de esto es exclusivo de Node. Puedes usar cualquier +ejecutor de pruebas que pueda importar un módulo ES. + +:::tip Ponle a tus archivos de prueba la extensión `.test.mjs` + +La salida compilada es `.mjs` y no necesita configuración. Pero tus archivos de +_prueba_ son cosa tuya: si les pones la extensión `.js` en un proyecto sin +`"type": "module"`, Node te avisará de que tiene que volver a analizarlos como +módulos ES. Si los llamas `.test.mjs`, evitas el aviso sin tocar tu +`package.json`. + +::: + + + + + ```js title="workflows/sms-parser/parse-message.js" + export const FIELDS = ['id', 'name', 'dob', 'weight']; + + export const parseSms = text => { + const parts = text.trim().split('#'); + return FIELDS.reduce((record, field, i) => { + record[field] = parts[i]?.trim() ?? null; + return record; + }, {}); + }; + + fn(state => ({ + ...state, + data: state.data.messages.map(parseSms), + })); + ``` + + + + + Después de `openfn compile --exports-only`: + + ```js title="dist/sms-parser/parse-message.mjs" + export const FIELDS = ['id', 'name', 'dob', 'weight']; + + export const parseSms = text => { + const parts = text.trim().split('#'); + return FIELDS.reduce((record, field, i) => { + record[field] = parts[i]?.trim() ?? null; + return record; + }, {}); + }; + ``` + La operación `fn(...)` desapareció. Las dos exportaciones se conservaron. + + + + Fíjate en la ruta del import: apunta a `dist/`, **no** a tu archivo fuente. + + ```js title="test/parse-message.test.mjs" + import { test } from 'node:test'; + import assert from 'node:assert/strict'; + + import { parseSms } from '../dist/sms-parser/parse-message.mjs'; + + test('parses a well-formed message into a record', () => { + assert.deepEqual(parseSms('P-001#Ada Lovelace#1815-12-10#3.2'), { + id: 'P-001', + name: 'Ada Lovelace', + dob: '1815-12-10', + weight: '3.2', + }); + }); + ``` + + + + +### Ejecutar la prueba {#running-the-test} + +```bash +openfn compile --exports-only && node --test +``` + +``` +✔ parses a well-formed message into a record (0.9ms) +✔ trims whitespace around each field (0.1ms) +✔ fills missing trailing fields with null (0.1ms) +ℹ tests 1 +ℹ pass 1 +ℹ fail 0 +``` + +## Paso 4: ejecuta las pruebas en modo watch {#step-4-running-test-in-watch-mode} + +Ejecuta el compilador en modo watch en una terminal: + +```bash +openfn compile --exports-only --watch +``` + +Y tu ejecutor de pruebas en modo watch en otra: + +```bash +node --test --watch +``` + +Ahora, cada vez que editas un step, este se vuelve a compilar, lo que cambia un +archivo en `dist/` y vuelve a ejecutar tus pruebas. + +## Páginas relacionadas {#related-pages} + +- [Compilación](/documentation/jobs/compilation): qué hace el compilador y por + qué +- [Buenas prácticas](/documentation/jobs/best-practices): cómo escribir código + de jobs que valga la pena probar +- [Uso básico de la CLI](/documentation/cli-usage): cómo ejecutar workflows en + tu computadora +- [OpenFn Sync](/documentation/sync): cómo descargar un proyecto para tener un + `openfn.yaml` que compilar diff --git a/i18n/es/docusaurus-plugin-content-docs/current/jobs/using-cursors.md b/i18n/es/docusaurus-plugin-content-docs/current/jobs/using-cursors.md new file mode 100644 index 000000000000..4054cb452c1f --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/jobs/using-cursors.md @@ -0,0 +1,205 @@ +--- +sidebar_label: Usar cursores +title: Usar cursores +translation_source_hash: da397a43bea2cd3ef29dc7ba2da3c9fecceeaf7e +translation_review_status: machine +--- + +## Usar cursores {#using-cursors} + +A veces conviene mantener una posición de cursor móvil sobre la fuente de datos +del backend. Por ejemplo, en un workflow con trigger cron, puedes usarla para +consultar en la base de datos los registros nuevos desde el último run. + +En un workflow cron, OpenFn pasa el state anterior al siguiente, así que el +state se conserva entre runs. Puedes aprovecharlo para retomar donde te +quedaste. + +Para facilitar el manejo del cursor, puedes usar la operación +[`cursor()`](/adaptors/packages/common-docs#cursor), que viene incluida en la +mayoría de los adaptors. + +
+Versiones compatibles + +La operación cursor se agregó a @openfn/language-common en la +versión 1.13.0 (publicada en abril de 2024). + +Los adaptors que usan common 1.12.0 o anterior no admiten la +operación cursor. Considera actualizar a la versión más reciente del adaptor +para aprovechar esta funcionalidad. + +
+ +### Definir el valor del cursor {#setting-the-cursor-value} + +Para usar un cursor desde una fecha fija, solo agrega una línea como esta al +principio de tu job: + +```js +cursor('2024-04-08T12:00:00.0000'); +``` + +Con un valor de texto como este, el cursor usará _siempre_ la fecha que +indicaste. + +Si usas un cursor de fecha, también puedes pasarle cadenas en lenguaje natural +como "now", "today", "yesterday", "24 hours ago" o "start" (es decir, la hora en +que empezó el job). + +:::tip Zonas horarias + +Las fechas relativas como "today" se convierten en un Date de JavaScript con la +configuración regional del sistema. + +Si usas la CLI, las horas se calculan con la hora local de tu sistema; si lo +ejecutas en Lightning, se usa la hora del sistema de Lightning (normalmente +UTC). + +La función cursor registra en el log la hora exacta que usa, con la zona +horaria. + +::: + +Para usar un cursor móvil o manual, deberías pasar el valor del cursor desde el +state. Quizás también quieras incluir un valor predeterminado: + +```js +cursor(state => state.cursor, { defaultValue: '2024-04-08T12:00:00.0000' }); +``` + +### Usar el cursor {#using-the-cursor} + +Para usar el cursor en tu job, solo usa `state.cursor` en tus consultas, como +cualquier otra propiedad del state. + +El uso cambia según el adaptor. Así podrías armar una URL con parámetros de +consulta usando el adaptor HTTP: + +```js +get(state => `/registrations?since=${state.cursor}`); +fn(/* do something good with your data */); +``` + +Esto lee el valor del cursor del objeto state, lo inserta en una cadena y lo +pasa a una consulta HTTP. + +O quizás quieras incluir el cursor en un objeto: + +```js +get('registrations', state => { + query: { + fromdate: state.cursor; + } +}); +``` + +El valor de un cursor puede ser cualquier cosa: una cadena, un Date, un número +de página, un objeto o lo que prefieras. + +Quizás quieras avanzar el cursor al final de un job, para dejarlo listo para el +siguiente run: + +```js +cursor(state => state.cursor, { defaultValue: 'today' }); +get(`/registrations?since={date.cursor}`); +fn(/* do something good with your data */); +cursor('now'); +``` + +### Cursores manuales {#manual-cursors} + +A menudo conviene definir la posición del cursor a mano, normalmente al probar o +depurar. Quizás el run de ayer falló y quieres repetirlo, o estás probando una +funcionalidad nueva y quieres experimentar con distintos cursores. + +Para hacerlo, define un valor de cursor en el state de entrada, así: + +```js +{ + "cursor": "today", +} +``` + +Puedes hacerlo al iniciar un run manual en el +[Job Inspector](/build/steps/step-editor.md) de la plataforma, o pasando el +state como entrada a la CLI: + +```bash +$ openfn job.js -s state.json -a http +``` + +
+Cursores manuales en v1 + +La plataforma v1 no permite definir libremente el state de entrada, así que +definir un cursor manual es un poco más difícil. + +Tienes que escribir el cursor manual directamente en el run para que se ignore +el cursor del state: + +```js +cursor('2024-03-12'); +``` + +Deberías comentar esta línea en los runs de producción. + +También puedes usar la opción defaultValue. Funciona siempre que ejecutes sin +ningún state inicial: + +```js +cursor(state => state.cursor, { defaultValue: '2024-03-12' }); +``` + +
+ +### Opciones del cursor {#cursor-options} + +El segundo argumento de `cursor()` es un objeto de opciones. Puedes usarlo para +definir el `defaultValue` o la `key` que debe usar el cursor (por defecto, +`cursor`): + +```js +cursor(state => state.cursor, { defaultValue: '2024-03-12', key: 'page' }); +``` + +### Dar formato al valor {#formatting-the-value} + +Si usas un servicio que no sigue los formatos de fecha estándar, o quieres +convertir varios formatos de entrada a un estándar común, puedes usar la opción +`format`. + +`format` recibe una función que toma como argumento el valor actual del cursor y +devuelve un valor con formato o actualizado. Se llama justo antes de asignar el +cursor al state. + +Por ejemplo, para usar un Date de JavaScript como cursor: + +```js +cursor('today', { format: c => new Date(c) }); +``` + +La función de formato se ejecuta después de procesar el lenguaje natural, así +que puedes interceptar el valor y convertirlo en lo que necesites. + +Puedes combinarla con +[`dateFns.format`](https://date-fns.org/v3.6.0/docs/format) para usar una marca +de tiempo personalizada: + +```js +cursor('today', { format: c => dateFns.format(new Date(c), 'dd/mm/yyyy') }); +``` + +Puedes agregar toda la lógica que quieras a la función de formato; es una +función normal de JavaScript: + +```js +cursor('today', { + format: c => { + if (typeof c === 'number') { + return { page: c, count: 20 }; + } + return c; + }, +}); +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/keyboard-shortcuts.md b/i18n/es/docusaurus-plugin-content-docs/current/keyboard-shortcuts.md new file mode 100644 index 000000000000..642c08630624 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/keyboard-shortcuts.md @@ -0,0 +1,44 @@ +--- +title: Atajos de teclado +keywords: [keystrokes, keyboard shortcuts, shortcuts] +translation_source_hash: 5d12e36efd8be3046bc306dc66a9aa22d367c006 +translation_review_status: machine +--- + +Los atajos de teclado (combinaciones de teclas) te permiten realizar acciones +comunes sin quitar las manos del teclado. 🤓 + +## Atajos de la plataforma {#platform-shortcuts} + +| Comando | Disponibilidad | Mac | Linux/Windows | Notas | +| ----------------------------------------------- | -------------------- | ---------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | +| **Cambiar** de proyecto | En cualquier lugar | `⌘+p` | `Ctrl+p` | Abre el selector de proyectos para cambiar entre tus proyectos (es decir, espacios de trabajo seguros) | +| **Expandir o contraer** el menú lateral | En cualquier lugar | `⌘+m` | `Ctrl+m` | | +| **Mostrar u ocultar** el AI Assistant | Canvas, IDE | `⌘+k` | `Ctrl+k` | | +| **Guardar** el workflow | Canvas, IDE | `⌘+s` | `Ctrl+s` | | +| **Guardar** y sincronizar el workflow | Canvas, IDE | `⌘+Shift+s` | `Ctrl+Shift+s` | Al elegir sincronizar, se te pedirá que escribas un mensaje de commit o que uses el mensaje predeterminado. | +| **Ejecutar** | Canvas, IDE | `⌘+Return` | `Ctrl+Enter` | Guarda tu workflow y lo ejecuta desde el step actual, con el comportamiento predeterminado de agrupación en work orders.\* | +| **Ejecutar** _(acción alternativa)_ | Canvas, IDE | `⌘+Shift+Return` | `Ctrl+Shift+Enter` | Guarda tu workflow y abre el diálogo de entrada personalizada del run o (si ya está abierto) crea una nueva work order desde el step actual. | +| **Mostrar u ocultar** el IDE (editor de código) | Canvas, IDE | `⌘+e` | `Ctrl+e` | Abre el editor de código a pantalla completa para el job seleccionado. | +| **Mostrar u ocultar** Run History | Canvas, IDE | `⌘+h` | `Ctrl+h` | No está disponible al crear un workflow nuevo. | +| **Cerrar** el panel o el IDE | Canvas, IDE | `Escape` | `Escape` | Cierra el Inspector, el IDE o el panel del run, si están abiertos. | +| **Mostrar u ocultar** el panel Templates | Creación de workflow | `⌘+/` | `Ctrl+/` | Solo está disponible al crear un workflow nuevo. | +| **Mostrar u ocultar** el panel Import | Creación de workflow | `⌘+\` | `Ctrl+\` | Solo está disponible al crear un workflow nuevo. Permite importar desde YAML. | + +\*Si estás viendo un run existente y creas un run nuevo desde el Canvas o el +IDE, ese run se asocia a la work order existente: este es el comportamiento +predeterminado. (Piénsalo como un "reintento"). A veces, por motivos de +auditoría, conviene crear una work order completamente nueva. Para hacerlo, usa +el botón de ejecución alternativa. + +## Algunos atajos del editor {#selected-editor-shortcuts} + +Consulta la paleta de comandos (haz clic derecho o presiona `F1`) para ver la +lista completa. Para usar estos atajos, tienes que hacer clic dentro de un +editor concreto: el IDE tiene varios editores. + +| Comando | Disponibilidad | Mac | Linux/Windows | +| ----------------------------- | --------------- | ---------------- | ------------- | +| Ver los comandos del editor | IDE, Run Viewer | `F1` | `F1` | +| Formatear el código | IDE | `Shift+Option+F` | `Shift+Alt+F` | +| Comentar o descomentar código | IDE | `⌘+/` | `Ctrl+/` | diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/collaboration.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/collaboration.md new file mode 100644 index 000000000000..323dd8dd3e63 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/collaboration.md @@ -0,0 +1,80 @@ +--- +title: Colaboración +sidebar_label: Colaboración +slug: /collaboration +translation_source_hash: e1e45b885085466371f4e864cd0b6210f1e153d5 +translation_review_status: machine +--- + +OpenFn permite que usuarios técnicos y no técnicos colaboren de forma eficaz y +se mantengan alineados al diseñar y gestionar los workflows de un proyecto. Esto +es posible gracias al Canvas, un editor visual de workflows, y a otras +funcionalidades de colaboración, como el control de versiones, la incorporación +de colaboradores y el uso compartido de credenciales, entre otras. Esta guía te +explica cómo gestionar los colaboradores de un proyecto. + +### ¿Quiénes son los colaboradores de un proyecto? {#who-are-project-collaborators} + +Un **colaborador de proyecto** es cualquier persona que tiene permisos +administrativos de edición o de visualización en un proyecto de OpenFn. A cada +colaborador se le asigna UNO de los cuatro roles principales en un proyecto al +que puede acceder, como se resume en la siguiente tabla: + +| Rol | Descripción | +| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Owner | El usuario que creó el proyecto | +| Admin | Un usuario que no es el propietario del proyecto, pero tiene acceso sin restricciones al proyecto y a sus workflows. También tiene permiso para agregar otros colaboradores al proyecto. | +| Editor | Un usuario con acceso a un proyecto que puede editar los workflows y la configuración del proyecto. El rol Editor es más limitado que el rol Admin | +| Viewer | Un usuario con acceso a un proyecto, pero limitado a ver la configuración y los artefactos del proyecto. | + +Puedes obtener más información sobre los permisos de cada rol +[aquí](/manage-projects/user-roles-permissions.md). + +### Agregar colaboradores al proyecto {#add-project-collaborators} + +Un usuario con rol Owner, Admin o Editor en un proyecto puede invitar a nuevos +colaboradores a su proyecto de OpenFn desde la página `Settings` del proyecto. + +Para agregar como colaborador a un usuario que ya tiene cuenta en OpenFn: + +1. Ve a la página `Settings` del proyecto y abre la pestaña `Collaboration` +2. Haz clic en el botón `Add Collaborator(s)` +3. Escribe el correo electrónico del usuario y selecciona el `Role` (Viewer, + Editor o Admin). +4. Agrega más colaboradores con el botón `Add Additional Collaborator`. También + puedes quitar a uno de los colaboradores con el botón de menos (-). +5. Haz clic en el botón `Save Collaborator` para guardar los cambios. + +Si alguno de los correos electrónicos que escribiste no tiene una cuenta de +OpenFn asociada, se te pedirá que autorices a OpenFn a crearle una cuenta y +enviarle una invitación a tu proyecto. Haz clic en `Invite new user` para +continuar con la invitación. + +![Colaboración](/img/collaboration.webp) + +![Agregar colaborador](/img/add_collab.webp) + +![Invitar a nuevos usuarios](/img/invite-new-users.webp) + +:::note + +Un proyecto tiene exactamente _un_ Owner, y no puedes asignar el rol Owner a +otro colaborador. Si necesitas cambiar el Owner del proyecto, contacta a tu +superadministrador o escribe a [support@openfn.org](mailto:support@openfn.org). + +::: + +### Quitar un colaborador {#removing-a-collaborator} + +Para quitar a un colaborador de un proyecto, un Owner o un Admin puede hacer +clic en el botón `Remove Collaborator` de la página `Collaboration` y confirmar +la eliminación en la ventana emergente. No se puede quitar al Owner de un +proyecto. + +:::tip + +En la página de colaboradores del proyecto también puedes configurar las alertas +de fallos y los resúmenes de tus proyectos. Obtén más información +[en esta guía](/manage-projects/notifications.md). + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/io-data-storage.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/io-data-storage.md new file mode 100644 index 000000000000..93d4f1088406 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/io-data-storage.md @@ -0,0 +1,105 @@ +--- +title: Almacenamiento de datos +translation_source_hash: 968a9459539d0d27aef3fa4496b6c80ef163d706 +translation_review_status: machine +--- + +En la sección "Data Storage" de los `Settings` de tu proyecto, puedes configurar +qué quieres que haga OpenFn con los _datos_ reales (`Inputs` y `Outputs`) que +procesan (o producen) los runs de tus workflows. + +### ¿Por qué guardaría los datos de entrada y salida junto con los logs de los runs? {#why-would-i-store-inputoutput-data-along-with-run-logs} + +Puedes configurar OpenFn para que guarde datos temporalmente (por ejemplo, +formularios obtenidos de la aplicación móvil de CommCare) y así poder +diagnosticar y corregir fácilmente las transacciones cuando haya errores (por +ejemplo, si el sistema DHIS2 de destino no está disponible o si una restricción +o validación de la base de datos bloquea una importación de datos). El +administrador de la instancia de OpenFn define un período de retención de datos +predeterminado, pero puede modificarse según los requisitos de cada proyecto. + +Una de las funcionalidades más potentes de la plataforma es la posibilidad de +"reproducir" work orders. Si tienes un workflow de varios steps (por ejemplo, +obtener datos de una base de datos, transformar y mapear los datos, e +importarlos a tu sistema de información de salud), este almacenamiento temporal +de datos permite a los administradores de OpenFn diagnosticar rápidamente las +work orders fallidas y "reintentar" el workflow desde el step que falló, en +lugar de volver a ejecutarlo desde el principio. Así, los administradores pueden +reprocesar las work orders fallidas sin tener que obtener (o volver a enviar) +los datos (entradas) desde un sistema de origen. + +### ¿Por qué elegiría _NO_ guardar los datos de entrada y salida? {#why-would-i-choose-to-_not_-store-inputoutput-data} + +Algunos de nuestros usuarios procesan datos extremadamente sensibles (como +historias clínicas) y quizás quieran asegurarse de que, una vez ejecutado un +workflow, no queden datos de pacientes en los servidores de OpenFn. + +Habilitar esta funcionalidad de "persistencia cero" ("zero-persistence") para +los datos de entrada y salida es una opción atractiva para quienes quieren usar +OpenFn en la nube, pero les preocupa la soberanía de los datos. + +:::tip + +Consulta la página de documentación sobre +[seguridad y cumplimiento](/get-started/security-compliance.md) para saber más +sobre el almacenamiento de datos y las arquitecturas de soluciones que se basan +en pipelines de datos de OpenFn con "persistencia cero". + +::: + +### Exportar el historial {#export-history} + +También puedes exportar todas las work orders de un proyecto y sus artefactos +asociados (runs, steps, runsteps y dataclips de entrada y salida). La +exportación del historial de work orders se gestiona a nivel de proyecto y está +disponible para todos los colaboradores del proyecto (viewer, editor, admin, +owner). + +#### Cómo exportar el historial de work orders {#how-to-export-work-order-history} + +Para exportar el historial de work orders de tu proyecto, abre el proyecto y haz +clic en `History` en el menú lateral. En la página History, desplázate hasta el +final de la tabla del historial de work orders y haz clic en el ícono de la nube +(mira la imagen de abajo). + +![Página History](/img/history_page_cloud.webp) + +Al hacer clic en el ícono de descarga, aparece una ventana modal para confirmar +la exportación. Si confirmas, se inicia un proceso en segundo plano para la +exportación. + +![Confirmar la exportación](/img/confirm_export.webp) + +Cuando termine la exportación, se enviará un correo electrónico a la dirección +asociada a tu usuario de OpenFn. + +:::info PARA DESPLIEGUES LOCALES + +En los despliegues locales, OpenFn usa Swoosh como servicio de buzón para +desarrollo, y puedes acceder al buzón en http://localhost:4000/dev/mailbox. +Puedes cambiar localhost:4000 por el puerto donde se aloja tu instancia de +OpenFn. + +::: + +#### Gestionar las exportaciones {#managing-exports} + +Puedes ver todas las exportaciones del historial en la página `History Exports` +de la configuración del proyecto. Haz clic en `Settings` en el menú lateral y +luego en `History Exports` para ver la lista de exportaciones de work orders de +tu proyecto. + +En la página `History Exports` verás la lista de exportaciones, con tu solicitud +más reciente y las anteriores, junto con otros datos como el nombre del archivo, +la fecha de exportación, el usuario que la solicitó y el estado. + +![Lista de exportaciones del historial](/img/history_exports_page.webp) + +:::caution Configurar el almacenamiento de las exportaciones + +En los despliegues locales, los administradores de la instancia de OpenFn pueden +configurar dónde se guardan las exportaciones de work orders. Actualmente, +OpenFn admite el almacenamiento local y Google Cloud Storage como destinos para +exportar work orders. + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/link-to-gh.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/link-to-gh.md new file mode 100644 index 000000000000..de677ffd414a --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/link-to-gh.md @@ -0,0 +1,334 @@ +--- +title: GitHub Sync +sidebar_label: GitHub Sync +slug: /link-to-GitHub +translation_source_hash: 4ae70e41e6cd18383fc6ee3bdcde51b4d49be923 +translation_review_status: machine +--- + +GitHub Sync permite una sincronización bidireccional entre un proyecto de OpenFn +y tu repositorio de GitHub. + +Esto significa que los cambios que hagas en un proyecto en OpenFn pueden +guardarse como commits en tu repositorio de GitHub, y los commits que subas a +GitHub pueden actualizar el proyecto en tu aplicación de OpenFn. + +:::info Para usuarios de OpenFn en la nube + +GitHub Sync solo está disponible en proyectos con planes Core, Growth, Scale o +Custom. + +::: + +### Configurar tu proyecto para usar GitHub Sync {#configuring-your-project-to-use-github-sync} + +Puedes configurar tus proyectos para que accedan a uno o más repositorios de +GitHub. Tienes que tener acceso de administrador al repositorio de GitHub para +poder instalar la aplicación de OpenFn. + +Para configurar tu proyecto para usar la sincronización con GitHub, sigue estos +pasos: + +1. Ve a `Project Settings > Sync to GitHub`. + +2. Si todavía no conectaste tu cuenta de usuario de OpenFn a GitHub, hazlo con + el botón **"Connect your OpenFn account to GitHub"**. + +![Configurar](/img/connect-account-to-github.webp) + +3. Elige qué instalación de GitHub usar para tu proyecto, o sigue el consejo de + abajo para actualizar tus instalaciones. + + :::tip + + Si no ves ninguna instalación, o si las que hay no tienen acceso a los + repositorios que quieres, haz clic en el enlace **"Create/update GitHub + installations or modify permissions"** para gestionar la instalación de + OpenFn en GitHub. Para ello tendrás que darle permisos a la aplicación de + OpenFn para acceder a tu cuenta y tu repositorio de GitHub. Si necesitas + ayuda, consulta + [Gestionar los permisos de GitHub](#managing-github-permissions). + + Cuando termines, puedes volver aquí y actualizar las listas con el botón 🔄 + que está junto a las listas desplegables. + + ::: + +4. Elige el repositorio y la rama a los que quieres conectar tu proyecto. + +![Configurar](/img/github-options.webp) + +5. **_Opcionalmente_**, si _primero quieres sincronizar de GitHub a OpenFn y ya + tienes un archivo de configuración_, agrega la ruta de un archivo + `config.json` de un proyecto existente. + + :::caution La mayoría de los usuarios deja "Path to config" en blanco. + + Esta funcionalidad avanzada te permite conectarte a un repositorio de GitHub + que _ya_ tiene un `project.yaml` y un `config.json` de OpenFn. (La mayoría de + las personas puede saltarse este paso.) Es útil cuando quieres que la primera + sincronización traiga datos de GitHub a OpenFn. La mayoría de los usuarios + prefiere que la primera sincronización salga _de_ OpenFn y que la aplicación + cree por ellos los archivos `config.json` y `project.yaml` necesarios. + + ::: + +6. Elige la **dirección** de la _primera_ sincronización. Es decir, cuando se + establezca esta conexión, ¿quieres que la integración _primero_ envíe una + copia de tu proyecto de OpenFn a GitHub, o que _primero_ sobrescriba tu + proyecto de OpenFn con un `project.yaml` que ya está en GitHub? + + :::warning Elegir desplegar _primero_ de "GitHub to OpenFn" es destructivo + + De forma predeterminada, tomamos lo que tienes en tu proyecto actual de + OpenFn y lo enviamos a GitHub para empezar el control de versiones. Si en + cambio eliges tomar un archivo `project.yaml` de GitHub y sobrescribir tu + proyecto actual de OpenFn, no podrás recuperar tus workflows existentes en + OpenFn. Esta funcionalidad cubre ciertos casos de uso avanzados y, a menos + que sepas lo que estás haciendo, deberías empezar sincronizando de "OpenFn to + GitHub". + + ::: + +7. Haz clic en **"Connect Branch & Initiate First Sync"** para terminar. Una vez + hecho esto, puedes ir a GitHub (con el enlace que se te da) para ver tu + proyecto de OpenFn como código (y empezar a trabajar con él). + +## Gestionar los permisos de GitHub {#managing-github-permissions} + +El acceso de la aplicación de OpenFn a tus repositorios de GitHub se concede _en +GitHub_, no en OpenFn. En la interfaz te damos un enlace para instalar y +gestionar estos permisos. Después de hacer clic en ese enlace, puedes seguir +estos pasos: + +1. Haz clic en **"Configure"** o **"Install"**. + +![Configurar](/img/lightning_gh_configure.webp) + +2. Luego selecciona la cuenta de GitHub propietaria del repositorio al que + quieres conectarte. + +![Instalar](/img/lightning_gh_install_openfn.webp) + +3. Selecciona el repositorio que quieres sincronizar y haz clic en **"Save"**. + +![Permisos](/img/lightning_gh_permissions.webp) + +4. Cuando termines de hacer cambios en GitHub, vuelve a OpenFn y actualiza las + listas de conexiones con el botón 🔄 que está junto a la lista desplegable de + instalaciones disponibles. + +## Usar el control de versiones y gestionar los cambios {#using-version-control--managing-changes} + +La funcionalidad `Sync to GitHub` usa GitHub Actions para desplegar +automáticamente (después de un commit en GitHub) o traer (cuando se hace clic en +el botón **"Initiate Sync to Branch"** en OpenFn) los cambios del proyecto, y +así mantener un repositorio sincronizado con tu proyecto de OpenFn. + +### Sincronizar de OpenFn a GitHub {#sync-from-openfn-to-github} + +Esta sincronización envía a GitHub los cambios de tu proyecto de OpenFn. Esta +operación de sincronización dispara un workflow de acción `openfn pull` en tu +repositorio de GitHub conectado, que trae la configuración más reciente de la +aplicación de OpenFn y la guarda como código en el archivo `project.yaml` de tu +repositorio. + +:::info + +Tu proyecto de OpenFn puede representarse como código y empaquetarse como +project.yaml, lo que se llama la especificación del proyecto (project spec). +Consulta la [documentación sobre portabilidad](/deploy/portability.md) para +saber más. + +::: + +Una vez que hayas configurado correctamente la conexión de tu proyecto con +GitHub como se explica [arriba](#managing-github-permissions), puedes iniciar +las siguientes sincronizaciones desde el Canvas, desde el Inspector o desde la +página de control de versiones de la configuración del proyecto. + +Para iniciar una sincronización desde el Canvas o el Inspector, presiona +`Ctrl+Shift+s` (o `⌘+Shift+s` en Mac; consulta los +[atajos de teclado](/keyboard-shortcuts.md)). También puedes hacer clic en el +ícono desplegable junto al botón de guardar y seleccionar `Save & Sync`. Al +hacer clic en Save & Sync, verás una ventana modal de confirmación con la opción +de personalizar el mensaje del commit. + +![Iniciar Save & Sync](/img/save-and-sync.webp) + +:::info La sincronización es una acción "a nivel de proyecto" + +Cuando ejecutas `Save & Sync` en un workflow, tus cambios nuevos y los cambios +_anteriores_ sin commit (si los hay) en los recursos de tu proyecto (incluidos +otros workflows) se guardan como commit en GitHub. Es decir, si tú u otra +persona hicieron cambios sin commit en otros workflows del proyecto, también +aparecerán en esa sincronización. + +::: + +Para sincronizar tu proyecto con GitHub desde la configuración del proyecto: + +1. Ve al proyecto donde editaste tus workflows y luego a la página + `Project Settings` +2. Desde la configuración del proyecto, ve a la página `Version Control` + haciendo clic en `Sync to GitHub` +3. Haz clic en el botón `Initiate Sync to Branch` para disparar una + sincronización con el repositorio de GitHub conectado + +![Iniciar la sincronización con GitHub](/img/sync_to_github.webp) + +### Sincronizar de GitHub a OpenFn {#sync-from-github-to-openfn} + +Usa este método de sincronización cuando quieras traer a OpenFn una versión de +tu proyecto desde GitHub. Cuando se dispara esta sincronización, se ejecuta la +acción `openfn-deploy` en GitHub y la especificación de tu proyecto _(el archivo +que termina en `.yaml`)_ se despliega automáticamente en OpenFn. + +:::tip Qué tener en cuenta al sincronizar cambios de GitHub a OpenFn + +Desde la v2.7.19, las acciones de despliegue y pull de OpenFn admiten rutas +relativas en la especificación del proyecto. Por eso, los proyectos cuya +estructura de directorios usa rutas relativas para el código de los jobs en la +especificación del proyecto se empaquetan y despliegan automáticamente, sin que +tengas que copiar los cambios a la especificación del proyecto. Este nuevo +enfoque da a los desarrolladores más flexibilidad para gestionar mejor el código +de sus jobs en archivos individuales, en lugar de tener todo el código en el +archivo `projectSpec.yaml`. + +Encontrarás más información sobre las rutas relativas y la estructura de +directorios en la +[documentación sobre portabilidad](/deploy/portability-v3.md#directory-structure). + +::: + +### Usar Sync v2 {#using-sync-v2} + +De forma predeterminada, GitHub Sync usa la estructura de carpetas antigua para +representar tu proyecto en GitHub. Esa estructura de carpetas se explica más +abajo: crea los archivos `config.json`, `state.json` y `project.yaml` para +representar tu proyecto en tu repositorio de git. + +En su lugar, puedes elegir el formato de sincronización v2, que se describe en +las páginas de [Sync](/build-for-developers/cli-sync.md). Este formato "expande" +automáticamente tus workflows y steps en archivos fáciles de leer y escribir, y +ofrece una experiencia mucho mejor para los desarrolladores. + +Este estilo v2 pronto será la forma predeterminada de sincronizar proyectos. + +También puedes crear un archivo `openfn.yaml` vacío en un repositorio ya +conectado, y la siguiente sincronización generará la estructura de archivos v2. + +:::warning + +En Sync v1, puedes tener varias sincronizaciones bidireccionales en la misma +rama de un mismo repositorio. Esto es así porque cada proyecto crea su propio +conjunto de artefactos de sincronización (config.json, project.yaml y +state.json). Normalmente esto se hace para sincronizar tus proyectos de +producción y de staging, o varios sandboxes, con el mismo repositorio de GitHub. + +Esto no funciona con el nuevo protocolo de sincronización, porque la nueva +sincronización comparte una carpeta `workflows`. Así que cada vez que GitHub +trae los cambios de tu proyecto, sobrescribe `workflows` y borra el estado de +tus otros proyectos. + +Para hacer esto en Sync v2, puedes: + +- Mantener una sincronización bidireccional por rama. Cada sandbox mantiene su + propio GitHub Sync con una rama distinta de tu repositorio. Esto funciona muy + bien, porque puedes comparar las diferencias entre tu sandbox y el proyecto + principal comparando las ramas en git. +- Conectar muchos proyectos a una rama, siempre que solo se sincronicen en un + sentido. Esto funciona en un entorno de producción donde un proyecto se + replica en varios despliegues, de modo que un commit en GitHub dispara una + actualización en todos los proyectos conectados. Funciona siempre que puedas + garantizar que ningún usuario hará Save & Sync desde los proyectos de + producción. + +::: + +## ¿Qué hay en tu repositorio de GitHub? {#what-is-in-your-github-repository} + +:::info + +Esta documentación describe el formato antiguo de GitHub Sync. El formato más +reciente se describe en las páginas de +[CLI Sync](/build-for-developers/cli-sync.md) y pronto se usará de forma +predeterminada. + +::: + +Cuando inicias una conexión entre OpenFn y tu repositorio de GitHub, se crea +automáticamente en la rama que indicaste un archivo config.json, que contiene +una referencia a los archivos de especificación y de estado de tu proyecto, y el +endpoint de tu despliegue de OpenFn. De forma predeterminada, OpenFn nombra +todos tus archivos con el UUID de tu proyecto en OpenFn, así que verás archivos +como estos: + +```json +{ + "endpoint": "https://app.openfn.org", + "specPath": "openfn-fdfdf286-aa8e-4c9e-a1d2-89c1e6928a2a-spec.yaml", + "statePath": "openfn-fdfdf286-aa8e-4c9e-a1d2-89c1e6928a2a-state.json" +} +``` + +Puedes editar el archivo config.json para adaptarlo a tu estructura de carpetas, +siempre que apunte a la especificación, el estado y el endpoint de OpenFn +correctos. Abajo tienes un ejemplo de archivo config.json con un nombre +personalizado para la especificación y el estado del proyecto. + +```json +{ + "endpoint": "https://app.openfn.org", + "statePath": "./custom-name-for-project-state.json", + "specPath": "./custom-name-for-project-spec.yaml" +} +``` + +## Solución de problemas {#troubleshooting} + +### Error de GitHub Sync: Unexpected inputs provided: ["snapshots"] {#github-sync-error-unexpected-inputs-provided-snapshots} + +Si instalaste GitHub Sync antes del 17 de julio de 2024, quizás tengas que +actualizar tu archivo `.github/workflows/openfn-pull.yml` para que quede así: + +``` +on: + workflow_dispatch: + inputs: + projectId: + description: 'OpenFN Project ID' + required: true + apiSecretName: + description: 'OpenFN API Key secret name i.e OPENFN_project_API_KEY' + required: true + pathToConfig: + description: 'Path to config.json' + required: true + branch: + description: 'Branch to commit the project state and spec' + required: true + commitMessage: + description: 'Commit message for project state and spec' + required: true + snapshots: + description: 'IDs of snapshots separated by spaces' + required: false + +jobs: + pull-from-lightning: + runs-on: ubuntu-latest + permissions: + contents: write + name: A job to pull changes from Lightning + steps: + - name: openfn pull and commit + uses: openfn/cli-pull-action@v1.1.0 + with: + secret_input: ${{ secrets[inputs.apiSecretName] }} + project_id_input: ${{ inputs.projectId }} + config_path_input: ${{ inputs.pathToConfig }} + branch_input: ${{ inputs.branch }} + commit_message_input: ${{ inputs.commitMessage }} + snapshots_input: ${{ inputs.snapshots }} +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/manage-credentials.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/manage-credentials.md new file mode 100644 index 000000000000..ca36080ead89 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/manage-credentials.md @@ -0,0 +1,189 @@ +--- +title: Gestionar credenciales +translation_source_hash: 7eb4c7e31c608628f42b86d45097f9e1d16c88d0 +translation_review_status: machine +--- + +Puedes ver las credenciales relacionadas con un proyecto en la página +`Settings > Credentials` del proyecto. En este artículo aprenderás a gestionar +las credenciales relacionadas con un proyecto. + +## Ver todas las credenciales del proyecto {#view-all-project-credentials} + +En la página `Credentials` puedes ver una lista de todas las credenciales, con +su nombre, tipo y propietario, y si son para un entorno de producción. + +![Resumen de credenciales](/img/lightning_credentials_overview.webp) + +:::info Ver los secretos de las credenciales + +Todos los colaboradores del proyecto pueden ver el nombre, el tipo y el +propietario de una credencial, pero solo su propietario puede ver los secretos +(nombre de usuario, contraseña, etc.). + +::: + +## Crear una credencial nueva {#create-a-new-credential} + +1. Haz clic en el botón `New Credential` y elige el tipo de aplicación que + quieres conectar. +2. Si tu aplicación no aparece en la lista, elige "Raw JSON" para crear tu + propia credencial personalizada o entrada de "configuración". Por ejemplo: + +```json +{ "loginUrl": "https://random-app.com", "username": "test", "password": "pwd" } +``` + +![Tipo de credencial](/img/lightning_choose_cred_type.webp) + +3. Haz clic en `Configure Credentials` y agrega los datos de autenticación de tu + aplicación. El formulario de la credencial indica qué campos son + obligatorios. + +![Agregar credencial](/img/lightning_add_cred.webp) + +:::tip ¿No sabes cómo completar todos los datos de la credencial? + +Si al crear una credencial nueva no sabes qué piden algunos campos (por ejemplo, +"security token"), ve a la página de documentación del [adaptor](/adaptors) +correspondiente para saber más y consulta su "configuration schema", o pregunta +en la [comunidad](https://community.openfn.org). + +::: + +4. Haz clic en `Save` y la verás en la lista de tu página `Credentials`. Ya + puedes usarla en todo el proyecto al crear y ejecutar workflows. + +![Credencial nueva lista](/img/lightning_new_cred_ready.webp) + +## Credenciales de Keychain (autenticación variable) {#keychain-credentials-variable-auth} + +Las credenciales de Keychain permiten que un mismo job use varias credenciales. + +Funcionan inspeccionando los datos del state del job en tiempo de ejecución (es +decir, state.data) y buscando el valor de un identificador predeterminado. Según +ese valor, presente en los datos de un mensaje de origen concreto, por ejemplo, +se selecciona y se aplica otra credencial para ese run del job. + +Imagina que tienes 2 credenciales en tu proyecto: + +1. Taylor’s Login, External ID: abc123, Body: + `{ username: “tay”, password: “shhhhh” }` + +2. Roina’s Login, External ID: def456, Body: + `{ username: “ro”, password: “veryshh” }` + +Y un job que usa una "Keychain Credential" con la ruta `$.data.myId`. + +Si un job de tu workflow usa la "Keychain Credential" y el dataclip inicial de +un run es así: + +```json +{ + "data": { + "content": "Hello world", + "myId": "abc123" + } +} +``` + +La credencial de Keychain buscará abc123 en las credenciales de tu proyecto y le +pasará esos secretos al mismo job. Es decir, el job se ejecutará con la +credencial "Taylor’s Login". + +Si se ejecuta otro run y su dataclip inicial es: + +```json +{ + "data": { + "content": "Goodbye!", + "myId": "def456" + } +} +``` + +El mismo job se ejecutará con la credencial "Roina’s Login". + +:::info Notas y limitaciones + +Como los secretos de las credenciales se obtienen al inicio de un run (no al +inicio de un step), actualmente no es posible resolver credenciales de Keychain +a partir de datos que se agregan al state más adelante en el run. Es decir, los +datos tienen que estar en el dataclip de entrada de todo el run, no en el +dataclip de entrada del step que usa la credencial de Keychain. + +::: + +### Crear una credencial de Keychain {#create-a-keychain-credential} + +1. En la página `Credentials` de la configuración del proyecto, haz clic en el + ícono desplegable del botón `Add New` y selecciona la opción Keychain: + + ![](/img/keychain_credential_dropdown.webp) + +2. Ponle un nombre a tu credencial de Keychain y asígnale una expresión + JSONPath. También puedes seleccionar una credencial predeterminada para usar + cuando la expresión JSONPath no encuentre coincidencias: + + ![](/img/keychain_modal.webp) + +3. Asigna un ID externo al que pueda acceder tu Keychain, creando una credencial + nueva o editando una existente: + + ![](/img/assign_externalID.webp) + +4. Ahora, en un job de tu workflow, puedes seleccionar y usar una credencial de + Keychain: + + ![](/img/keychain_selection.webp) + +5. Ya puedes hacer referencia a tu Keychain en tu entrada para usarla: + + ![](/img/keychain_input.webp) + +## Compartir credenciales {#share-credentials} + +Si eres propietario de una credencial, puedes elegir qué proyectos tienen acceso +a ella. Para actualizar los proyectos con los que compartes tu credencial, sigue +los pasos de la +[página de documentación de credenciales de usuario](/manage-users/user-credentials.md). + +## Credenciales `Raw JSON` {#raw-json-credentials} + +Las credenciales raw son documentos JSON válidos que se pasan al state del job +en tiempo de ejecución. Ten en cuenta que los propietarios de estas credenciales +pueden verlas completas y sin cifrar. + +Las credenciales raw funcionan con cualquier adaptor, siempre que la credencial +especifique las claves de `configuration` que ese adaptor exige (por ejemplo, +`baseUrl`). Consulta la documentación del "configuration schema" de cada adaptor +para ver qué se necesita para esa aplicación. + +:::info Usa `Raw JSON` para entradas personalizadas en la credencial + +Usa el tipo de credencial `Raw JSON` si quieres guardar secretos que no son +entradas estándar del formulario de credencial de un adaptor. Por ejemplo, si tu +API REST necesita un `client_id` en lugar de un `username`, tu esquema de +`configuration` podría parecerse al fragmento de código de abajo. Como +`client_id` no es una opción del formulario de credencial `Http` predeterminado, +puedes crear tu propia credencial personalizada con el tipo `Raw JSON`. + +::: + +Ejemplo del cuerpo o `configuration` de una credencial Raw JSON: + +```json +{ + "baseUrl": "https://myapp.com/api", + "client_id": "test-j01", + "password": "testing123", + "customInput": "whateverYouWant" +} +``` + +## Seguridad de las credenciales {#credentials-security} + +Todas las credenciales se guardan cifradas en reposo, y solo sus propietarios +pueden ver los secretos. Consulta la +[documentación de seguridad](/get-started/security-compliance.md) de OpenFn para +más información. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/notifications.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/notifications.md new file mode 100644 index 000000000000..cbd586906858 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/notifications.md @@ -0,0 +1,46 @@ +--- +title: Notificaciones de fallos y resúmenes +sidebar_label: Notificaciones por correo electrónico +slug: /notifications +translation_source_hash: 388be709d62844f7f65a6592d669a5e1b49983df +translation_review_status: machine +--- + +Si quieres recibir `Notifications` por correo electrónico cuando falla un run de +un workflow, o `Digests` por correo con un resumen de la actividad de tu +proyecto, sigue leyendo para saber cómo configurar tu proyecto. + +Este artículo te explica cómo configurar las notificaciones por correo +electrónico para hacer seguimiento de tus workflows. + +### Alertas de fallos {#failure-alerts} + +En `Project Settings > Collaboration` puedes habilitar las alertas de fallos +para recibir notificaciones por correo electrónico cuando falla un job. + +![Alerta de fallo](/img/lightning_failure_alert.webp) + +La notificación por correo incluye los logs y un enlace al run fallido, para que +puedas inspeccionarlo y empezar a diagnosticar el problema. + +![Correo de fallo](/img/lightning_failure_email.webp) + +![Run fallido](/img/lightning_failed_run.webp) + +### Resúmenes por correo electrónico {#email-digests} + +También en `Project Settings > Collaboration` puedes elegir recibir resúmenes +diarios, semanales o mensuales de un proyecto por correo electrónico, con los +runs correctos y fallidos de cada uno de tus workflows. + +![Configuración del resumen por correo](/img/lightning_digest.webp) + +![Resumen por correo](/img/lightning_weekly_digest.webp) + +:::note + +Si quieres ajustar la configuración de tus notificaciones y eres colaborador en +más de 1 proyecto, tendrás que ir a la página `Project Settings > Collaboration` +de _cada_ proyecto al que perteneces. + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/oauth.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/oauth.md new file mode 100644 index 000000000000..7c0a53e243fc --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/oauth.md @@ -0,0 +1,164 @@ +--- +title: Autenticación OAuth +sidebar_label: Autenticación OAuth +slug: /oauth +translation_source_hash: 1db156d8415788d53449388418ee834ee5a733c2 +translation_review_status: machine +--- + +Algunas aplicaciones exigen [OAuth](https://oauth.net/2/) como método de +autenticación para conectarse con aplicaciones de terceros y hacer solicitudes a +través de sus API. OpenFn te permite conectarte con aplicaciones mediante su +autenticación OAuth. Para usar esta funcionalidad en tus workflows de OpenFn, +tienes que configurar clientes y credenciales OAuth para tus instancias o +proyectos. Esta guía te explica cómo gestionar los clientes y las credenciales +OAuth. + +## Clientes OAuth {#oauth-clients} + +### ¿Qué es un cliente OAuth y cuándo lo necesito? {#what-is-an-oauth-client-and-when-do-i-need-it} + +Al configurar OAuth para una aplicación, autorizas a OpenFn a conectarse e +interactuar con esa aplicación dentro de un conjunto de permisos (scopes) que tú +defines. Por ejemplo, podrías configurar una autorización OAuth para que OpenFn +se conecte a tu cuenta de Google Sheets y lea y siga los cambios en tu nombre. +En este ejemplo, tienes que configurar un cliente de OpenFn que represente una +instancia de OpenFn en Google y que reúna todos los permisos que OpenFn necesita +en tu nombre. Todas las solicitudes y respuestas de la API pasan por el cliente +de OpenFn y se autorizan con un token de autorización que guarda el cliente. + +En la mayoría de los casos basta con configurar un cliente por aplicación, pero +según los requisitos del proyecto y las políticas de la organización, puede +haber varios clientes configurados para una misma aplicación. Estos clientes +pueden pertenecer al mismo usuario de OpenFn o a usuarios distintos, y pueden +estar disponibles para proyectos distintos. + +Por cada aplicación que necesites conectar a OpenFn, tienes que configurar al +menos un cliente para tus proyectos. + +Los clientes OAuth se pueden configurar en la +[página de credenciales del proyecto](/manage-projects/manage-credentials.md) o +en la [página de credenciales del usuario](/manage-users/user-credentials.md). + +### Crear un cliente OAuth {#creating-an-oauth-client} + +Si todavía no tienes un cliente OAuth configurado para tu proyecto, verás una +sección vacía con un botón que te invita a crear un cliente, como se muestra a +continuación. + +![Nuevo cliente](/img/create_new_oauth_client.webp) + +Si no, verás la lista de clientes OAuth a los que tienes acceso. Para crear un +cliente nuevo, haz clic en el botón `Add new` y selecciona +`OAuth client [Advanced]` en el menú desplegable. + +![Menú desplegable de OAuth](/img/oauth_dropdown.webp) + +:::tip + +Asegúrate de agregar https://app.openfn.org/authenticate/callback como URL de +callback de la aplicación cuando habilites la autenticación OAuth en la +aplicación de terceros. (Nota: si no usas app.openfn.org, reemplaza +`https://app.openfn.org/` por la URL base de _tu_ despliegue de OpenFn). + +Para ver indicaciones específicas de cada aplicación (por ejemplo, cómo +configurar un cliente OAuth [para Google Sheets](/adaptors/googlesheets)), +consulta la [documentación del adaptor](/adaptors) correspondiente. + +::: + +### Compartir clientes OAuth {#sharing-oauth-clients} + +Un superusuario puede compartir clientes OAuth con proyectos de dos formas: + +1. Hacer que un cliente sea global +2. Compartirlo con proyectos concretos + +Puede hacerlo en la ventana modal de configuración del cliente OAuth, al crear +el cliente o al editarlo. + +![Editar cliente OAuth](/img/oauth_client_edit.webp) + +### Hacer que los clientes OAuth sean globales {#making-oauth-clients-global} + +Cuando un cliente OAuth es global, los usuarios de la instancia pueden acceder a +él y crear credenciales a partir de él. + +Para que un cliente sea global, baja hasta la sección `Manage Project Access` de +la ventana modal de configuración del cliente OAuth, marca la casilla +`Make client global (allow any project in this instance to use this client)` y +guarda los cambios. Todos los proyectos de la instancia tendrán acceso al +cliente, y los usuarios con rol Owner, Admin o Editor en esos proyectos podrán +crear credenciales a partir de él. + +![Acceso de proyectos al cliente OAuth](/img/manage_project_access.webp) + +### Compartir clientes OAuth con proyectos {#sharing-oauth-clients-with-projects} + +Para compartir un cliente OAuth con proyectos concretos, baja hasta la sección +`Manage Project Access` de la ventana modal de configuración del cliente OAuth. +Abre el menú desplegable de proyectos, selecciona un proyecto y haz clic en el +botón para agregarlo y darle acceso al cliente. + +![Compartir cliente OAuth](/img/share_oauth_client.webp) + +## Credenciales OAuth {#oauth-credentials} + +### Crear una credencial a partir de un cliente OAuth {#creating-a-credential-from-an-oauth-client} + +Cada cliente necesita un token de autenticación para autenticar las solicitudes +que se hacen a la aplicación en nombre del usuario. En OpenFn, estos tokens se +crean como credenciales y se asocian a los clientes. + +1. Para crear una credencial a partir de un cliente OAuth, haz clic en el botón + "Add new" y selecciona `Credential` en el menú desplegable, o haz clic en el + botón `create a new credential`. + +![Nueva credencial](/img/oauth_dropdown.webp) + +![Crear nueva credencial](/img/create_new_cred.webp) + +2. Luego, en la ventana modal de tipo de credencial, busca y selecciona el + cliente OAuth que quieres usar para crear la credencial OAuth. Se abrirá una + nueva ventana modal para que configures la credencial con el nombre, los + permisos (scopes) necesarios y la versión de la API. +3. Cuando hayas completado el formulario, haz clic en el botón + `Sign in with [your OAuth Client name]` para autorizar el cliente OAuth. Este + botón abre una nueva pestaña para que le concedas a OpenFn un token de + autorización con el que autenticar tus solicitudes. + +:::note + +Después de iniciar sesión, tendrás que darle acceso a OpenFn haciendo clic en +`Allow` en la ventana modal de permisos. Ten en cuenta que esto puede verse +distinto según la aplicación, pero el objetivo es darle permiso a OpenFn para +realizar ciertas acciones en la aplicación en tu nombre. El usuario que +autentica los clientes OAuth debería tener los permisos necesarios en la +aplicación. + +::: + +### Eliminar clientes y credenciales {#deleting-clients-and-credentials} + +Para eliminar una credencial o un cliente, haz clic en `Delete`. + +![Editar cliente OAuth](/img/oauth_client_edit.webp) + +Aparece un mensaje para que confirmes la acción. + +En cuanto confirmes que quieres eliminar una credencial, recibirás un correo +electrónico que te avisa de que la credencial quedó programada para eliminarse. + +La fecha de eliminación depende de un período de gracia que configura el +administrador de tu instancia. En la +[instancia alojada de OpenFn](https://app.openfn.org/), la credencial se elimina +de forma permanente al cabo de 7 días. + +### Más sobre la gestión de credenciales {#more-on-managing-credentials} + +Consulta la documentación sobre +[cómo gestionar las credenciales de usuario](/manage-users/user-credentials.md) +para saber más sobre la gestión de credenciales de las aplicaciones que integras +con OpenFn. + +### Ejemplo de configuración de un cliente OAuth {#example-oauth-client-configuration} diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/platform-mgmt.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/platform-mgmt.md new file mode 100644 index 000000000000..ce68d5953313 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/platform-mgmt.md @@ -0,0 +1,96 @@ +--- +title: Gestión de proyectos +translation_source_hash: e35856d95724118bd7c59b0f7f385392009870ac +translation_review_status: machine +--- + +## ¿Qué es un proyecto? {#what-is-a-project} + +Un `Project` en OpenFn es un espacio de trabajo compartido de un equipo u +organización, que contiene sus workflows, credenciales y colaboradores, todos +limitados a ese proyecto. + +## Gestionar proyectos {#managing-projects} + +En la versión `v2.7.14` agregamos una tabla `Projects` para que los usuarios +puedan gestionar sus proyectos de OpenFn en forma de tabla. Es la página nueva +que verás cada vez que inicies sesión en tu cuenta de OpenFn. Cuando haces clic +en `Projects` en el menú lateral, ves la lista de proyectos a los que tienes +acceso como colaborador. + +![Tabla de proyectos](/img/projects-table.webp) + +## Crear un proyecto nuevo {#creating-a-new-project} + +Para crear un proyecto nuevo, sigue estos pasos: + +1. Inicia sesión en tu cuenta de OpenFn o, si estás en otro proyecto, ve a la + tabla de proyectos haciendo clic en `Projects` en la ruta de navegación. +2. En la tabla de proyectos, haz clic en `Create project`. Se abrirá una ventana + modal para que ingreses los datos del proyecto. +3. Escribe el `name` y la `description` del proyecto nuevo. +4. Si usas OpenFn en la nube, tendrás que seleccionar la `billing account` a la + que se facturará el proyecto nuevo. + +:::info Para usuarios en la nube en app.openfn.org + +1. Los proyectos de una misma cuenta de facturación deberían tener nombres + únicos. +2. Cada usuario tiene un proyecto inicial gratuito. Para crear un proyecto + nuevo, necesitas un método de pago válido y elegir un plan. + +::: + +![Ventana modal para crear un proyecto](/img/create-project-modal.webp) + +## Actualizar la información del proyecto {#updating-project-information} + +Puedes ver la información de tu proyecto en `Settings` (en el menú lateral de la +aplicación). Ahí puedes ver o editar el nombre y la descripción del proyecto. + +![Resumen del proyecto](/img/lightning_project_overview.webp) + +También puedes exportar todo tu proyecto "como código", ya sea para guardarlo o +para editarlo localmente. Encontrarás más información sobre esta funcionalidad +en nuestra [página de portabilidad](/deploy/portability.md). + +## Gestionar la concurrencia del proyecto {#managing-project-concurrency} + +OpenFn admite runs concurrentes de workflows y proyectos. Esto significa que +varios runs del mismo workflow o proyecto pueden ejecutarse al mismo tiempo, +siempre que estén configurados para ejecutarse en paralelo. + +Para gestionar la concurrencia del proyecto, usa la sección `Concurrency` de la +configuración del proyecto. + +![Concurrencia del proyecto](/img/configuring-project-concurrency.webp) + +Puedes habilitar o deshabilitar la ejecución en paralelo de un proyecto. Cuando +la ejecución en paralelo está deshabilitada, solo puede ejecutarse un run a la +vez de un workflow del proyecto. + +### Workflows en modo síncrono {#sync-mode-workflows} + +Ten en cuenta que los workflows que se disparan con un webhook y están +configurados para responder de forma síncrona suelen ejecutarse en una cola de +prioridad (para reducir los tiempos de solicitud y respuesta HTTP), según lo +configure el superusuario de tu instancia, e IGNORAN todos los límites de +concurrencia. Es decir, usan el número máximo de workers de prioridad +disponibles para reducir los tiempos de respuesta. + +:::warning Los workflows en modo síncrono ignoran la concurrencia del proyecto + +Usan el número máximo de workers disponibles, según lo configure el +administrador de tu instancia. + +::: + +### Limitador de concurrencia por workflow {#workflow-level-concurrency-limiter} + +:::info Concurrencia por proyecto frente a concurrencia por workflow + +La concurrencia por proyecto tiene prioridad sobre la concurrencia por workflow. +Esto significa que, si la ejecución en paralelo está deshabilitada en un +proyecto, se ignora la configuración de concurrencia de sus workflows. + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/retention-periods.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/retention-periods.md new file mode 100644 index 000000000000..3173e5d2b4b2 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/retention-periods.md @@ -0,0 +1,35 @@ +--- +title: Retención del historial +translation_source_hash: d1dcb2afd88e4d5f16731dcfe028463b30448485 +translation_review_status: machine +--- + +En la sección "Data Storage" puedes configurar durante cuánto tiempo quieres que +se guarde el historial de runs de un proyecto. + +:::tip + +Ten en cuenta que, si usas Lightning en OpenFn.org, el plan de un proyecto puede +limitar durante cuánto tiempo guardamos tu historial. Siempre puedes guardarlo +menos tiempo, y los planes de pago ofrecen un almacenamiento más largo. + +::: + +### ¿Qué es el historial? {#what-is-history} + +El historial incluye las work orders, los runs, los logs y los dataclips de +entrada y salida asociados. + +### ¿Por qué querría reducir el período de retención del historial de un proyecto? {#why-would-i-want-to-reduce-my-history-retention-period-for-a-project} + +Algunos administradores de proyectos deciden guardar el historial durante menos +tiempo (un mes o 90 días) para reducir los costos de almacenamiento de datos o +limitar su huella de datos. + +### ¿Qué pasa cuando se eliminan work orders, runs, logs o dataclips antiguos? {#what-happens-when-old-work-orders-runs-logs-or-dataclips-get-removed} + +Todo el historial se elimina de OpenFn y ya no se puede consultar en la +plataforma. Si antes solicitaste +[exportaciones del historial de work orders](/manage-projects/io-data-storage.md#export-history), +podrás seguir accediendo a los CSV exportados desde la página de exportaciones +del historial, en la configuración del proyecto. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/staging-prod.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/staging-prod.md new file mode 100644 index 000000000000..7e99a6c175a7 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/staging-prod.md @@ -0,0 +1,126 @@ +--- +title: Control de versiones para proyectos de staging y producción +sidebar_label: Proyectos de staging y producción +slug: /staging-prod +translation_source_hash: 87d54b2bb53b4c41a1fae83e0cb1cf5514e4d665 +translation_review_status: machine +--- + +Usar proyectos separados de producción y de staging (pruebas) para construir y +probar tus workflows antes de empezar a usarlos en producción es una práctica +segura y eficiente. El [control de versiones](/manage-projects/link-to-gh.md) +hace que este proceso sea fluido. Esta guía te explica cómo configurar tus +proyectos de OpenFn y tu repositorio de GitHub, y te da dos ejemplos de cómo +gestionar tu flujo `Staging > Production`: uno para proyectos nuevos y otro para +proyectos existentes en los que quieres agregar un proyecto y una rama de +staging. + +### Configuración para proyectos nuevos {#setup-for-new-projects} + +1. Primero, crea un proyecto `Production` y otro `Staging` en OpenFn (2 + proyectos) + +![Proyectos de producción y staging](/img/openfn_prod_staging.webp) + +2. Elige o crea un repositorio de GitHub para tu proyecto, y crea una rama + `staging` + +![Ramas main y staging](/img/staging_prod_branches_gh.webp) + +3. Conecta tus proyectos a las ramas `main` y `staging`, respectivamente. Sigue + [esta guía](/manage-projects/link-to-gh.md) para configurar la conexión +4. En cada repositorio, crea un archivo `.js` vacío para tu job. Asegúrate de + que tengan el mismo nombre y la misma ruta en cada repositorio (por ejemplo, + `upsert-contacts.js`). Estos archivos guardarán el código del job al que se + vincularán en el siguiente paso. + +5. Cuando conectaste las ramas a tus proyectos en el paso 3, se creó + automáticamente un archivo `spec.yaml` en la rama después de la primera + sincronización (junto con otros dos archivos de configuración). Abre estos + archivos en GitHub y busca tu job en el archivo. Reemplaza el contenido de + `body` por `path: {path to the related js file}`. Haz esto tanto en la rama + `main` como en la rama `staging`. + +![Spec de main](/img/path_main.webp) ![Spec de staging](/img/path_staging.webp) + +6. ¡Ya está todo configurado! +7. Para sincronizar un cambio de tu proyecto de staging a producción **con la + aplicación de OpenFn**, ve a tu proyecto `Staging` en OpenFn y edita tu job. + Luego ve a `Settings` > Sync to `GitHub` del proyecto y haz clic en + `Initiate Sync to Branch`. +8. También puedes editar directamente el código del job en GitHub y hacer commit + de los cambios en la rama `staging` en GitHub. +9. Cuando hayas hecho commit de los cambios en tu rama `staging`, verás en + GitHub un aviso de que hubo cambios recientes. Haz clic en + `Compare & pull request`. + +![Crear pull request](/img/staging_pushes.webp) + +10. Crea una pull request. Incluirá automáticamente todos los cambios que se + hicieron en los archivos de la rama staging. + +![Guardar pull request](/img/create_pr.webp) + +11. Según el flujo de trabajo de GitHub de tu equipo, pide a alguien que apruebe + y haga merge de la pull request, o haz clic en `Merge pull request`. + +12. Tus cambios se desplegarán automáticamente en tu proyecto `Production` de + OpenFn (vinculado a la rama `main` de GitHub). + +### Configuración para proyectos existentes {#setup-for-existing-projects} + +1. Primero, asegúrate de que el código de todos tus jobs esté guardado en + archivos `.js` separados (como `Notify-CHW-upload-successful.js`) en GitHub, + vinculados en tu `spec.yaml` de esta forma: + +```yaml + +Notify-CHW-upload-successful: + name: Notify-CHW-upload-successful + adaptor: '@openfn/language-http@latest' + enabled: true + # credential: + # globals: + body: | + path: ./workflow/Notify-CHW-upload-successful.js + +``` + +Encontrarás más información sobre esta configuración en nuestra +[documentación de GitHub](/manage-projects/link-to-gh.md#sync-from-github-to-openfn). + +2. Con esto listo, crea una nueva rama `staging` en GitHub a partir de tu rama + de producción `main`, que guarda tu proyecto actual. Para hacerlo, en tu + repositorio de GitHub, entra en `Branches` (donde dice `1 Branch` en la + captura de pantalla de abajo). + +![Ramas](/img/1_branch.webp) + +3. Haz clic en `New branch`, ponle un nombre como `staging` y, si ya tienes + varias ramas, asegúrate de que el origen sea `main`. Luego haz clic en + `Create new branch`. + +![Nueva rama](/img/new_branch.webp) + +4. Ve a tu nueva rama `staging`. **Este paso es importante. Fíjate en que la + nueva rama contiene ahora los 3 archivos de configuración (`config.json`, + `spec.yaml` y `state.json`) que estaban en la rama main. Elimínalos de la + rama `staging`.** En los pasos siguientes se crearán otros nuevos, propios de + la rama staging. + +5. Ahora ve a OpenFn y crea un nuevo proyecto `Staging`. + +6. Sigue [esta guía](/manage-projects/link-to-gh.md) para configurar la conexión + de GitHub con tu rama `staging`, y haz clic en `Initiate a sync` (desde la + página `Settings > Sync to GitHub` del proyecto). Esto creará los archivos de + configuración necesarios en la rama de GitHub. + +7. En el archivo `spec.yaml` recién generado en la rama `staging` de GitHub, + vincula los archivos `.js` de tus jobs como se explica en el paso 1. + +8. Cuando inicies una nueva sincronización desde OpenFn, el código de los jobs + de los workflows configurados en la aplicación se sincronizará con los + archivos de job de OpenFn correspondientes en GitHub. + +9. Para hacer cambios futuros en tu proyecto "Staging", sigue los pasos 7 a 12 + de la sección `Setup for new projects` de esta guía. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/user-roles-permissions.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/user-roles-permissions.md new file mode 100644 index 000000000000..c1c002e1b7be --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/user-roles-permissions.md @@ -0,0 +1,59 @@ +--- +title: Roles y permisos de usuario +sidebar_label: Roles de usuario +translation_source_hash: ac3a8fdca96392acaf666020cf7c03ebdf796adf +translation_review_status: machine +--- + +Cuando invitas a usuarios de OpenFn a trabajar en tu proyecto como +`Collaborators`, se les asigna un `Role` que determina sus permisos. Los cuatro +roles disponibles son: Owner (**solo 1 por proyecto**), Admin, Editor y Viewer. +Consulta la tabla de abajo para ver los permisos de cada rol. + +| Contexto | Acción | Owner | Admin | Editor | Viewer | +| :-------- | :--------------------------------------------------------------------------- | :----------------- | :----------------- | :----------------- | :----------------- | +| Workflows | Crear un workflow | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | +| Workflows | Editar un job de un workflow | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | +| Workflows | Agregar o quitar un método de autenticación de webhook de un workflow | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +| Workflows | Eliminar un workflow | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | +| Workflows | Ejecutar desde el Inspector | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | +| Workflows | Seleccionar las 5 entradas más recientes de un job de un workflow | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | +| History | Ver, buscar y filtrar en la página History | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| History | Ver un run desde el historial de work orders | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| History | Ver una entrada desde el historial de work orders | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| History | Ejecutar desde el historial de work orders | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | +| Settings | Ver el nombre del proyecto | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Settings | Editar el nombre del proyecto | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +| Settings | Ver la descripción del proyecto | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Settings | Editar la descripción del proyecto | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +| Settings | Exportar el proyecto | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Settings | Eliminar un proyecto | :heavy_check_mark: | :x: | :x: | :x: | +| Settings | Ver las credenciales del proyecto, su tipo y su propietario | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Settings | Agregar o quitar un método de autenticación de webhook del proyecto | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +| Settings | Cambiar el requisito de MFA del proyecto | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +| Settings | Agregar o quitar colaboradores del proyecto | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +| Settings | Ver los colaboradores del proyecto (project_users, rol, resúmenes y alertas) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Settings | Editar sus propios resúmenes y alertas | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Settings | Editar los resúmenes y alertas de otros | :x: | :x: | :x: | :x: | +| Settings | Cambiar la política de almacenamiento de dataclips de entrada y salida | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +| Settings | Cambiar el período de retención del historial | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +| Settings | Actualizar la conexión del proyecto o repositorio de GitHub | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | +| Settings | Iniciar la sincronización con GitHub | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | + +### Privilegios de superusuario {#super-user-privileges} + +Cada instancia de OpenFn tiene un usuario con el rol de superusuario, que le da +control administrativo total de la plataforma. Esto incluye la gestión de +usuarios, proyectos, el registro de auditoría y la autenticación de terceros, +con los siguientes privilegios de superusuario: + +| Aspecto | Descripción | Funcionalidades y permisos | +| --------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| Gestión de usuarios | La gestión de los usuarios de una instancia de OpenFn | Crear, editar y eliminar usuarios | +| Gestión de proyectos | Cómo se crean y gestionan los proyectos en la instancia | Crear, eliminar y editar un proyecto, y asignarle usuarios | +| Autenticación | Gestión del acceso de terceros para los usuarios de la instancia | Configurar OpenID Auth para la instancia | +| Registro de auditoría | Auditabilidad y gestión de cambios | Ver el historial de las acciones relevantes de los usuarios en la instancia para auditorías | + +Si usas la plataforma de OpenFn alojada (por ejemplo, app.openfn.org), escribe a +[support@openfn.org](mailto:support@openfn.org) si necesitas contactar al +superusuario para solicitar proyectos nuevos o cambios de configuración. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/webhook-auth.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/webhook-auth.md new file mode 100644 index 000000000000..9d04ca7f5200 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/webhook-auth.md @@ -0,0 +1,77 @@ +--- +title: Seguridad de webhooks +sidebar_label: Seguridad de webhooks +slug: /webhook-security +translation_source_hash: dc4bb5877d05c57f51762f307fbc80288cb9db96 +translation_review_status: machine +--- + +Esta página te guía por los pasos para agregar una capa extra de seguridad a tu +webhook. + +## Agregar un método de autenticación de webhook {#adding-a-webhook-authentication-method} + +En tus proyectos de OpenFn puedes usar webhooks para recibir datos de +aplicaciones externas con un [trigger webhook](/build/triggers.md). Para más +seguridad, cuando usas un webhook puedes exigir que las aplicaciones externas se +autentiquen antes de enviar datos a tu proyecto. + +OpenFn admite la autenticación HTTP básica con nombre de usuario y contraseña, y +la autenticación con clave de API mediante el encabezado de solicitud +`x-api-key`. + +### Agregar autenticación desde `Project Settings` {#adding-authentication-via-project-settings} + +Puedes agregar un método de autenticación nuevo en `Webhook Security`, dentro de +los `Project Settings`. La autenticación que configures aquí se puede usar luego +en cualquiera de los workflows de este proyecto. + +![Webhook Security en Project Settings](/img/lightning_auth_project_settings.webp) + +Después de hacer clic en `New auth method`, elige el tipo: Basic HTTP o API Key +Authentication. + +![Método de autenticación nuevo](/img/lightning_choose_auth_method.webp) + +#### Basic Auth + +Para Basic Auth, ponle un nombre, elige un nombre de usuario y una contraseña, y +haz clic en `Create Auth Method`. + +![Basic Auth](/img/lightning_basic_auth.webp) + +#### API Key + +Para API Key, solo elige un nombre y haz clic en `Create Auth Method`. Se genera +una clave de API para ti. + +![Autenticación con API Key](/img/lightning_api_auth.webp) + +En esta página también puedes editar o eliminar tus métodos de autenticación. + +// screenshot + +Cuando agregas un método de autenticación a un webhook, aparece en +`Linked Triggers`. + +![Linked Triggers](/img/lightning_linked_triggers.webp) + +![Linked Triggers](/img/lightning_linked_triggers2.webp) + +### Agregar autenticación desde un workflow {#adding-authentication-via-a-workflow} + +En tus workflows puedes usar los métodos de autenticación que creaste en +`Project Settings`, o crear uno nuevo. + +Cuando hagas clic en `Add authentication`, en `Webhook Authentication`, +selecciona uno o varios métodos existentes, o haz clic en +`Create a new webhook auth method`. Consulta las secciones `Basic Auth` y +`API Key` de arriba para ver cómo agregarlos. + +Cuando agregas un método de autenticación, aparece en la configuración del +trigger webhook. + +![Linked Triggers](/img/lightning_workflow_trigger_added.webp) + +Solo las solicitudes que usen estos datos de autenticación obligatorios podrán +enviar datos a tu workflow. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/workflow-dashboard.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/workflow-dashboard.md new file mode 100644 index 000000000000..4afb55e2cb5d --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-projects/workflow-dashboard.md @@ -0,0 +1,27 @@ +--- +title: Monitoreo de workflows +sidebar_label: Monitoreo de workflows +slug: /monitoring-workflows +translation_source_hash: c45b9bb5f99bb94d614245195adb31c0b8b3bb3b +translation_review_status: machine +--- + +En este artículo aprenderás a monitorear el estado de tus workflows. + +## Panel de workflows {#workflow-dashboard} + +En la página principal de workflows puedes ver un resumen de la cantidad de work +orders y runs de tu proyecto, y de la cantidad y la proporción de éxitos y +fallos. + +![Panel de workflows](/img/lightning_workflow_dashboard.webp) + +Para investigar más a fondo, haz clic en la cantidad de work orders de un +workflow, como se muestra, y llegarás al History de ese workflow. Por ejemplo, +estas son las 7 work orders en estado fallido: + +![Work orders fallidas](/img/lightning_failed_work_orders.webp) + +Consulta nuestra +[documentación de History](/monitor-history/activity-history.md) para saber más +sobre cómo gestionar y monitorear runs y work orders. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-users/api-tokens.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-users/api-tokens.md new file mode 100644 index 000000000000..3242063e62a6 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-users/api-tokens.md @@ -0,0 +1,36 @@ +--- +title: Crear y administrar tokens de API +sidebar_label: Tokens de API +slug: /api-tokens +translation_source_hash: c69fe92dda6de7120298b55845a5d47a6e3103eb +translation_review_status: machine +--- + +Este artículo te explica cómo crear un token de API de acceso personal. + +### Acerca de los tokens de API {#about-api-tokens} + +OpenFn da permisos de API a los usuarios para que puedan crear sus proyectos en +la plataforma, o interactuar con ellos, a través de la API. Para acceder a la +plataforma a través de la API necesitas un token de acceso personal (Personal +Access Token). Para saber más sobre cómo crear o actualizar tu proyecto mediante +código, visita nuestra página [Portabilidad](/deploy/portability.md). + +Tu acceso a la API te da el mismo nivel de permisos que tienes como usuario en +OpenFn (por ejemplo, si tu perfil tiene acceso de nivel Admin, tu usuario de la +API también tendrá permisos de Admin). + +### Crear un token de API {#creating-an-api-token} + +Puedes administrar tus tokens en tu perfil de usuario. + +![API Tokens Profile](/img/lightning_user_profile_api_tokens.webp) + +![API Tokens](/img/lightning_no_api_token.webp) + +1. Haz clic en `Generate New Token` para crear uno nuevo. + +![New Token](/img/lightning_new_api_token.webp) + +2. Asegúrate de copiar tu token nuevo de inmediato. Después no podrás verlo ni + copiarlo. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-users/user-credentials.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-users/user-credentials.md new file mode 100644 index 000000000000..e04e75b2b7f0 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-users/user-credentials.md @@ -0,0 +1,55 @@ +--- +title: Administrar las credenciales de usuario +sidebar_label: Credenciales de usuario +slug: /user-credentials +translation_source_hash: 63ee09db3568f7693da2dbc1741446c246cb6182 +translation_review_status: machine +--- + +Puedes administrar todas las credenciales de las que eres propietario en la +página `Credentials` de tu perfil. En este artículo te explicamos cómo +administrar y compartir entre proyectos las credenciales que te pertenecen. + +### Todas tus credenciales en un solo lugar {#all-your-credentials-in-one-place} + +La página `Credentials` de tu `User Settings` te permite agregar, ver, editar o +eliminar las credenciales que te pertenecen. Es el lugar central para +administrar tus credenciales en todos los proyectos en los que colaboras. + +![User Credential](/img/lightning_user_profile_credentials.webp) + +![User Credentials List](/img/lightning_edit_user_credential.webp) + +Para saber cómo configurar una credencial nueva, consulta la página +[Administrar credenciales](/manage-projects/manage-credentials.md). + +Puedes actualizar el nombre y los datos de inicio de sesión de una credencial +después de hacer clic en `Edit`. + +![User Credential Edit View](/img/lightning_cred_edit_view.webp) + +### Compartir credenciales {#share-credentials} + +También puedes dar acceso a varios proyectos a una credencial que te pertenece. + +Para agregar o quitar el acceso de un proyecto, haz clic en `Edit` en la +credencial que quieres compartir y elige el proyecto en el menú desplegable de +`Project Access`. + +![Update Project Access](/img/lightning_share_cred_with_project.webp) + +:::info Las credenciales compartidas siguen siendo secretas + +Si compartes una credencial con un proyecto, los colaboradores de ese proyecto +pueden _usar_ la credencial en sus workflows, pero no pueden ver los datos de +inicio de sesión que contiene. + +::: + +:::tip + +Si quieres compartir los datos de inicio de sesión de la credencial, usa un +método seguro, como un administrador de contraseñas o una plataforma de +mensajería cifrada. + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/manage-users/user-profile.md b/i18n/es/docusaurus-plugin-content-docs/current/manage-users/user-profile.md new file mode 100644 index 000000000000..ede3416e746a --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/manage-users/user-profile.md @@ -0,0 +1,69 @@ +--- +title: Actualizar el perfil de usuario +sidebar_label: Perfil de usuario +slug: /user-profile +translation_source_hash: 599daf8fdede00b97cdb29723b9c6c392d68afea +translation_review_status: machine +--- + +Este artículo te explica cómo ver y actualizar tu información de usuario en tu +perfil de usuario. + +![User Profile](/img/lightning_select_user_profile.webp) + +### Cambiar el correo electrónico y la contraseña {#change-email-and-password} + +Puedes cambiar la dirección de correo electrónico asociada a tu perfil y +actualizar tu contraseña. + +![Change Email Password](/img/lightning_change_email_pw.webp) + +### Habilitar la autenticación multifactor {#enable-multi-factor-authentication} + +Al habilitar la autenticación multifactor, agregas una capa extra de seguridad a +tu cuenta, porque para iniciar sesión se necesita algo más que una contraseña. + +![Enable MFA](/img/lightning_enable_MFA.webp) + +Puedes vincular tu cuenta a una app de autenticación o a una extensión del +navegador, como 1Password o Authy. Una vez configurada, la app genera una +contraseña de un solo uso que tienes que ingresar al iniciar sesión para +verificar tu identidad cada vez. + +Para configurar la autenticación multifactor, usa una app de autenticación o una +extensión del navegador para escanear el código QR que aparece en tu perfil. + +También puedes configurarla ingresando en la app la clave secreta que se genera +en tu perfil. + +### Eliminar la cuenta {#account-deletion} + +En tu perfil de usuario también puedes eliminar tu cuenta de OpenFn. + +![Delete Account](/img/lightning_delete_account_cropped.webp) + +Para eliminar tu cuenta, haz clic en el botón **"Delete my account"**. Se te +pedirá que confirmes la eliminación ingresando tu dirección de correo +electrónico y haciendo clic en **"Delete Account"**. + +Cuando confirmas que quieres eliminar tu cuenta, se programa su eliminación +según el período de gracia que haya definido el administrador de tu instancia. + +:::info + +El período de gracia es el tiempo que tienes para cambiar de opinión y cancelar +la eliminación antes de que tu cuenta se elimine definitivamente. El valor +predeterminado es de 7 días. + +::: + +#### Eliminación de la cuenta y auditoría {#account-deletion-and-auditing} + +Ten en cuenta que, si usaste tu cuenta para crear work orders o runs +manualmente, no se eliminará de forma permanente de la instancia hasta que se +elimine esa actividad relacionada. En esos casos, formas parte del registro de +auditoría de un proyecto, y es posible que el administrador de la instancia no +pueda eliminar tu cuenta de forma permanente. + +Si usas https://app.openfn.org, tienes que cancelar cualquier suscripción activa +antes de poder eliminar tu cuenta. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/activity-history.md b/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/activity-history.md new file mode 100644 index 000000000000..6d88aee93e1b --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/activity-history.md @@ -0,0 +1,93 @@ +--- +title: History y búsqueda en OpenFn +sidebar_label: History y búsqueda +translation_source_hash: 598667faa8236bba7833a136f1086d7a837ae6d7 +translation_review_status: machine +--- + +Para los administradores de la plataforma, `History` es la consola central para +supervisar toda la actividad de tus workflows activos. Sigue leyendo para +conocer sus componentes principales. + +## History + +La página `History` muestra una lista de todas las +[work orders](/get-started/terminology.md#work-order) y los +[runs](/get-started/terminology.md#run) que se procesaron en un proyecto. + +![History](/img/case-referral-history.webp) + +## Ejecución de workflows: work orders y runs {#workflow-execution-work-orders-and-runs} + +Los workflows de OpenFn se ejecutan así: + +1. Un `Trigger` del workflow se activa con un evento de webhook, un temporizador + cron o una acción manual. +2. Esto crea una `Work Order`: una solicitud para ejecutar un workflow con una + entrada determinada (por ejemplo, el envío de un formulario nuevo o un + registro de paciente que hay que procesar). Para que una `Work Order` se + complete, debería llegar a un step final con éxito (sin errores); así se + garantiza que el procesamiento terminó. +3. Después se ejecuta un `Run` para intentar completar el workflow con éxito. + Este run tendrá un [código de estado](/monitor-history/status-codes.md) que + indica si los steps del workflow se procesaron correctamente. +4. Si el primer `Run` falla, puedes volver a ejecutarlo para "reintentar" el + workflow. Se creará un segundo `Run`. Si tiene éxito, tanto el run como la + work order relacionada se actualizarán con el estado `success`. + +También puedes **cancelar** runs pendientes o **reintentar** work orders +completadas directamente desde la página History. Consulta +[Reintentar y cancelar runs](/monitor-history/rerunning-workflow.md) para más +detalles. + +![History Page](/img/history-page-annotated.webp) + +Consulta las demás páginas de esta sección para saber más sobre cómo +inspeccionar runs, solucionar problemas y volver a ejecutar runs fallidos. + +## Cómo funciona la búsqueda {#how-search-works} + +Con la barra de búsqueda de la página History puedes encontrar work orders cuyos +dataclips de entrada o salida _relacionados_, o cuyos logs de runs, contienen +cadenas de texto específicas. De forma predeterminada, el sistema busca solo en +los logs de runs, pero puedes elegir buscar en cualquiera de estas tres +opciones, o en todas: + +![Search Options](/img/search-options.webp) + +1. UUIDs de OpenFn de work orders, runs o steps +2. Cuerpos de los dataclips de entrada y salida +3. Logs de runs + +Si buscas texto dentro de un dataclip de entrada o salida o de los logs de runs, +se aplica una búsqueda `tsvector`. Este método de búsqueda te permite encontrar +work orders rápidamente y admite coincidencias parciales en todo el texto de los +logs de runs y en las "keys" y los "values" de tus dataclips. + +:::caution Es posible que los dataclips de entrada muy grandes o complejos no se +indexen + +Actualmente no es posible crear índices `tsvector` de más de 1 MB, por lo que es +posible que los dataclips de entrada muy grandes o complejos no aparezcan en los +resultados de búsqueda. Por lo general esto no ocurre hasta que te acercas a los +10 MB de JSON, pero la cantidad de lexemas y posiciones distintos de tu JSON +influye en el tamaño final del índice. + +Más información en la página +["text search limitations"](https://www.postgresql.org/docs/current/textsearch-limitations.html) +de la documentación de Postgres. + +::: + +Las coincidencias parciales funcionan mejor al principio de las palabras, así +que si buscas elementos que coincidan con `"newPatient"`, es mejor buscar +`"newPat"` que `"tient"`. (Si tienes dudas, las palabras completas o los IDs dan +los mejores resultados). + +## Buscar y filtrar resultados {#search--filter-results} + +Aunque puedes buscar cadenas de texto que aparecen en logs de runs o dataclips +concretos, es importante recordar que los resultados que se devuelven siguen +siendo **work orders**. Si los dataclips de salida del tercer step del primer +run de la work order "123" coinciden con tu búsqueda, verás la work order "123" +en los resultados. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/inspect-runs.md b/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/inspect-runs.md new file mode 100644 index 000000000000..08e4607fd674 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/inspect-runs.md @@ -0,0 +1,31 @@ +--- +title: Inspeccionar runs y buscar desde la página History +sidebar_label: Inspeccionar runs +translation_source_hash: ca04c62187b3ec930749e2f0691da095925d7ac6 +translation_review_status: machine +--- + +Cada vez que OpenFn intenta ejecutar un workflow para una work order +determinada, se crea un [run](/get-started/terminology.md#run). Puedes ver, +filtrar y buscar todos los runs desde la página `History`. + +En pocas palabras, los runs nos dicen "qué pasó" cuando OpenFn intentó ejecutar +el workflow. Los runs tienen hora de inicio, hora de fin, logs y +[códigos de estado](/monitor-history/status-codes.md) que indican cuándo +ocurrieron, qué hicieron y si tuvieron éxito o no. + +## Inspeccionar runs {#inspect-runs} + +Mira el siguiente video tutorial +([o abre el enlace](https://youtu.be/xPgVZmJMT3w?si=bMf9wof_Qla-0ihW)) para ver +paso a paso cómo inspeccionar runs desde la página History. + + + +## Buscar en el historial y en los runs {#search-history-and-runs} + +Para aprender a buscar y filtrar el historial de work orders y runs desde la +página History, mira el siguiente video tutorial +([o abre el enlace](https://youtu.be/XIUykmLCxwQ?si=pCzefw4zyLxG1voE)). + + diff --git a/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/rerunning-workflow.md b/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/rerunning-workflow.md new file mode 100644 index 000000000000..8b7e841d0680 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/rerunning-workflow.md @@ -0,0 +1,110 @@ +--- +title: Reintentar y cancelar runs +sidebar_label: Reintentar y cancelar +translation_source_hash: c00e54f15cb364cb77cc6975b6465eb09f520470 +translation_review_status: machine +--- + +Desde la página `History` puedes realizar acciones sobre work orders y runs +según su estado actual. Usa **Retry** para volver a ejecutar work orders +completadas, o **Cancel** para quitar runs pendientes de la cola. + +## Acciones disponibles según el estado de la work order {#available-actions-by-work-order-state} + +| Estado de la work order | Acciones disponibles | +| :------------------------------------------------------------------------------------------- | :------------------- | +| **Pending** (runs en espera en la cola) | Cancel | +| **Running** | Ninguna | +| **Estados finales** (Success, Failed, Crashed, Killed, Exception, Lost, Cancelled, Rejected) | Retry, Retry from | + +:::info Seleccionar work orders con estados distintos + +Si seleccionas varias work orders de categorías de estado distintas (por +ejemplo, algunas pendientes y otras fallidas), los botones Retry y Cancel se +deshabilitan. Para usar las acciones en bloque, selecciona solo work orders de +la misma categoría de estado. + +::: + +## Reintentar una work order {#retry-a-work-order} + +¿Falló un step de tu workflow? ¿Quieres volver a sincronizar datos históricos? +Sea cual sea el motivo, mira el siguiente video tutorial +([o abre el enlace](https://youtu.be/DvLRA6kloNE?si=U0NMx-HsCMZxeJwg)) para +aprender a volver a ejecutar tu workflow. + + + +### Reintentar desde la página History {#retry-via-history-page} + +Para volver a ejecutar tu workflow desde la página `History`: + +1. Busca tu `Work Order` fallida (usa la barra de búsqueda o los filtros si hace + falta) +2. Contrae la work order para ver los `Runs` relacionados +3. Haz clic en `rerun` junto al step desde el que quieres volver a ejecutar el + workflow. Elige el primer step para empezar desde el principio, o un step + posterior para volver a ejecutar el workflow a partir de ese step. +4. Esto crea un nuevo `Run` asociado a la misma work order. Revisa el `Status` + para ver si este run completó la work order con éxito. + +### Reintentar desde la vista del Inspector {#retry-via-inspector-view} + +Para volver a ejecutar tu workflow desde la página `Inspector`: + +1. Busca tu `Work Order` fallida (usa la barra de búsqueda o los filtros si hace + falta) +2. Contrae la work order para ver los `Runs` relacionados +3. Haz clic en `inspect` junto al step que quieres abrir en la vista `Inspector` + para seguir investigando el problema. +4. Se abre la vista `Inspector`, donde puedes ver el `Input` y el `Output` del + step fallido. Si hace falta, puedes editar la lógica personalizada en el + panel `Editor`. +5. Cuando quieras reintentar el workflow con el mismo Input, haz clic en + `Rerun from here`. Esto crea un nuevo `Run` para la misma work order. Ve a la + página `History` y revisa el `Status` para ver si este run completó la work + order con éxito. +6. Si prefieres crear una work order _nueva_ (en lugar de reintentar la misma), + puedes hacer clic en el menú desplegable junto a "Rerun from here" y elegir + _en su lugar_ `Create New Work Order`. + +## Cancelar runs pendientes {#cancel-pending-runs} + +Si hay runs atascados en la cola o se crearon por error, puedes cancelarlos. Al +cancelarlos, los runs pasan de `available` a `cancelled` y el estado de la work +order correspondiente pasa de `pending` a `cancelled`. Consulta +[Códigos de estado](/monitor-history/status-codes.md) para saber qué significa +cada estado. + +Hay varias formas de cancelar: + +- **Cancelar todos los runs de una work order:** haz clic en el botón de acción + de una fila de work order pendiente en la página History para cancelar todos + sus runs pendientes. +- **Cancelar un solo run:** haz clic en el botón de cancelar junto a un run, ya + sea en la lista de runs o en la página de detalle del run. + +:::note Runs que empiezan antes de la confirmación + +Si un run pendiente empieza a ejecutarse entre el momento en que abres el +diálogo de confirmación de la cancelación y el momento en que confirmas, ese run +**no** se cancela. Solo se ven afectados los runs que siguen en la cola en el +momento de la confirmación. + +::: + +## Acciones en bloque {#bulk-actions} + +Puedes actuar sobre varias work orders a la vez seleccionándolas con las +casillas de verificación de la página History: + +- **Cancelar en bloque:** selecciona work orders en estado `Pending` y haz clic + en el botón `Cancel` para cancelar todos los runs pendientes de las work + orders seleccionadas. +- **Reintentar en bloque:** selecciona work orders en un estado final (por + ejemplo, Failed o Crashed) y haz clic en el botón `Retry`. + +Los botones de acciones en bloque solo se habilitan cuando todas las work orders +seleccionadas pertenecen a la misma categoría de estado. Si seleccionas work +orders con estados distintos (por ejemplo, algunas pendientes y otras fallidas) +o solo work orders en ejecución, se deshabilitan todos los botones de acción. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/status-codes.md b/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/status-codes.md new file mode 100644 index 000000000000..c78d0d1d6667 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/status-codes.md @@ -0,0 +1,58 @@ +--- +title: Códigos de estado de work orders y runs +sidebar_label: Códigos de estado +translation_source_hash: a81cb1fcb1e0bdd937dda745cf4db31bc9366f13 +translation_review_status: machine +--- + +## Estado de la work order {#work-order-status} + +Una `Work Order` es una solicitud para iniciar la ejecución de un workflow de +OpenFn con una entrada determinada (por ejemplo, "completar el workflow de +derivación del paciente 123"). Para una organización, la work order suele ser la +unidad de valor de negocio, porque los usuarios quieren asegurarse de que cada +solicitud de workflow se procesó con éxito. + +Como los administradores pueden querer ejecutar la misma work order varias veces +(por ejemplo, "intentar completar de nuevo el workflow de derivación del +paciente 123 ahora que el sistema de gestión de casos del gobierno volvió a +estar en línea"), el "estado" de una work order se determina por el estado del +_último_ run de esa work order. + +Es decir, si la work order "completar el workflow de derivación del paciente +123" se ejecutó dos veces y el primer run falló pero el segundo tuvo éxito, el +"estado" de esa work order será "success". + +## Estado del run {#run-status} + +Cada run tiene un estado que indica si se completó con éxito. + +| Estado | Indicador | Tipo | ¿Aborta el run?\* | Descripción o ejemplo | +| :-------- | :-------: | :----------------: | :---------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Pending | ⚪ | | - | El run está esperando a que haya un worker disponible para empezar a ejecutarse | +| Running | 🔵 | | - | El run sigue en curso | +| Success | 🟢 | | - | O todos los steps de este run tuvieron éxito _o_ cada error se manejó correctamente. Técnicamente, un run tiene éxito si el step final de cada rama (el nodo hoja) tiene éxito | +| Failed | 🔴 | JobError | No | Una solicitud falló con el código de estado 404 | +| Failed | 🔴 | TypeError | No | Intentar hacer referencia a `state.data.patient.age` cuando `state.data.patient` es `undefined` | +| Failed | 🔴 | RangeError | No | Llamar a `state.patients[5]` cuando solo existen 2 pacientes | +| Crashed | 🟠 | SyntaxError | Sí | Tienes JavaScript con errores y el worker no puede compilar el código de tu job | +| Crashed | 🟠 | ReferenceError | Sí | Tienes una variable sin declarar en el código de tu job | +| Cancelled | ⚪ | | Sí | El run estaba en la cola, pero se [quitó manualmente](/monitor-history/rerunning-workflow.md#cancel-pending-runs) | +| Killed | 🟡 | SecurityError | Sí | Tu código no pasó los controles de seguridad, por ejemplo, intentó usar `eval` | +| Killed | 🟡 | ImportError | Sí | Intentaste importar un módulo externo que no permitimos | +| Killed | 🟡 | OomError | Sí | Tu run usó más memoria de la que permite la instancia de Lightning | +| Killed | 🟡 | StateTooLargeError | Sí | Tu step devolvió un objeto `state` que superó el 25 % del límite total de memoria del run | +| Killed | 🟡 | TimeoutError | Sí | Tardó más que el tiempo máximo de ejecución que permite la instancia de Lightning | +| Exception | ⚫ | | Sí | Ocurrió un error que no esperábamos (se notificó al superusuario de la instancia) | +| Lost | ⚫ | | Sí | Lightning perdió la comunicación con el worker (se notificó al superusuario de la instancia) | +| Rejected | ⚪ | | - | El administrador de la instancia no procesará esta solicitud de run porque tu proyecto alcanzó su límite de runs | + +### \*Nota sobre el manejo de errores dentro de un workflow {#note-on-error-handling-within-a-workflow} + +Si un step del workflow falla (por ejemplo, con `JobError`, `TypeError` o +`RangeError`), el worker de OpenFn sigue procesando el workflow, ya que puede +haber reglas de manejo de errores en los edges posteriores. (Por ejemplo: "Si el +step 3 falla, ejecuta el step 4"). + +Si un step falla con un crash (por ejemplo, `SyntaxError`), el worker no puede +ejecutar ninguna lógica posterior y se aborta todo el attempt. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/troubleshooting.md b/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/troubleshooting.md new file mode 100644 index 000000000000..69a7459a5b65 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/monitor-history/troubleshooting.md @@ -0,0 +1,181 @@ +--- +title: Logs y solución de problemas +sidebar_label: Logs y solución de problemas +keywords: + - runs + - logs + - log levels + - status codes + - exit codes + - troubleshooting +translation_source_hash: 142023e6bcf41255617e547ab466c497c6e1266e +translation_review_status: machine +--- + +Esta página ofrece consejos para solucionar problemas a quienes usan la +_plataforma OpenFn v2_. + +## Runs + +Una de las páginas más útiles para solucionar problemas en OpenFn es la página +[History](/monitor-history/activity-history.md). Muestra una lista de todos los +runs ejecutados para una work order y su estado. Los administradores del +proyecto pueden investigar los errores haciendo clic en un run para revisar sus +detalles. Más información sobre los runs +[aquí](/monitor-history/inspect-runs.md). + +### Códigos de estado {#status-codes} + +Cada run tiene un código de estado. El código de estado es la forma en que +OpenFn clasifica el estado del run, y puede ayudarte a solucionar errores. Más +información sobre los códigos de estado de OpenFn y lo que significa cada uno +[aquí](/monitor-history/status-codes.md). + +### Cuánto tardó el workflow en fallar {#the-time-it-took-for-the-workflow-to-fail} + +El run también registra cuánto tiempo pasó antes de que el workflow fallara. +Este dato ayuda a saber si el workflow está tardando más de lo que debería, y es +especialmente útil con errores relacionados con tiempos de espera agotados. +Puedes usar el run para saber en qué operación se agota el tiempo del workflow y +si se puede optimizar su rendimiento. + +### Logs del run {#run-logs} + +Mientras desarrollas workflows, es importante registrar en los logs detalles que +harán mucho más fáciles las pruebas y la solución de problemas en el futuro. + +#### Niveles de log {#log-levels} + +![log-levels](/img/log-levels.webp) + +| Nivel | Descripción | +| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `debug` | Muestra todos los logs, incluidas las cosas de nivel de sistema que produce el "runtime" y la salida de una instrucción `console.debug()` escrita por el usuario. | +| `info` | El nivel de log predeterminado. Muestra la información clave que producen los adaptors o las instrucciones `console.log()`/`console.info()`. | +| `warn` | Oculta la mayor parte del ruido y solo muestra los eventos principales del run (inicio y fin de cada step), las advertencias de los adaptors o las instrucciones `console.warn()`. | +| `error` | Oculta todo excepto los eventos principales del run, los errores de los adaptors y las instrucciones `console.error()`. | + +#### Mapeos {#mappings} + +Si es posible, los logs deberían escribirse de forma que se vea exactamente qué +se mapeó entre el sistema de origen y el sistema de destino. En resumen, el log +puede tener una sección **"Datos recibidos del sistema de origen"** y una +sección **"Datos que se cargarán en el sistema de destino"**. + +Estos logs pueden ayudar a los administradores a verificar que los datos de +origen y los datos que se cargan en el sistema de destino son correctos. Por +ejemplo, ver en los logs que un identificador único se está mapeando a +`undefined` en el sistema de destino puede ayudarte a entender la causa raíz de +un error. Este mensaje de error de Salesforce podría deberse a un mapeo a +`undefined`: + +`METHOD_NOT_ALLOWED: HTTP Method 'PATCH' not allowed. Allowed are GET,HEAD,POST at HttpApi.getError`. + +#### Mensajes de error {#error-messages} + +El log del run también debería indicarnos si se lanzó un error y, según el +sistema de destino, cuál es el mensaje de error. A veces el mensaje de error es +muy específico, como: + +`NOT_FOUND: Provided external ID field does not exist or is not accessible` + +Este error de Salesforce suele indicar que `External ID` no está marcado en la +configuración del campo en Salesforce. + +Otros mensajes de error no son tan claros y pueden llevar un tiempo de +depuración: + +`TypeError [Error]: Cannot read property 'split' of undefined` + +Los **`TypeErrors`** suelen indicar que el job recibió una parte de la entrada +que no esperaba, o que hay un error de sintaxis en el código del job. Significa +que hay que actualizar el job para que sepa manejar esa entrada. En este caso, +el job recibió una versión antigua del formulario de CommCare a la que le +faltaba un campo sobre el que el job llamaba a la función `split`. Para +averiguarlo, revisa en qué campos del job se llama a la función split y +comprueba que todos estén presentes en el mensaje. + +Cuanto más pruebas y solucionas problemas con un sistema concreto, más te +familiarizas con sus mensajes de error. + +:::tip + +OpenFn describió varios de los mensajes de error más comunes de algunos de los +sistemas que integramos en el pasado. Explora estos sistemas y sus mensajes de +error [aquí](/adaptors). + +::: + +## Aprovechar la búsqueda y los filtros en OpenFn {#leveraging-search-and-filtering-in-openfn} + +Aprovecha las distintas funcionalidades de búsqueda de OpenFn para encontrar los +runs que te ayuden a solucionar problemas. En la página History puedes buscar +por IDs de OpenFn, entradas o logs. + +Mira este [video](https://youtu.be/XIUykmLCxwQ?si=hquc8rPTJrAZkbbD) para +aprender a usar la búsqueda. + +## Suscríbete a las alertas por correo electrónico {#sign-up-for-email-alerts} + +Puedes activar las notificaciones para recibir +[alertas por correo electrónico](/manage-projects/notifications.md) cuando un +workflow falle y suscribirte a resúmenes de la actividad del proyecto. + +## Más {#more} + +> ¿Qué pasa si los datos de mi encuesta de ODK tienen que vincularse con +> registros existentes en mi sistema Salesforce, pero alguien que responde +> ingresa o selecciona un `external ID` no válido? + +Buena pregunta, y no te preocupes: pasa todo el tiempo. Suponiendo que ya +tomaste todas las medidas posibles para precargar los external IDs en tu +formulario de ODK o para usar IDs más a prueba de errores humanos (como códigos +de barras y huellas digitales), este es el flujo de trabajo: + +1. Lee el correo electrónico e investiga el motivo del fallo. + +2. El 99 % de los runs fallidos en OpenFn se deben a `value mismatches`. El `id` + _recolectado_ en ODK no coincide con el `id` _esperado_ en Salesforce. Ahora + tienes que elegir entre: + + A. Editar el `id` de origen en tu `receipt` y reintentar el attempt. + + B. Editar el `id` relacionado en tu sistema de destino y reintentar el + attempt. + + C. Ignorar el attempt: estos datos de origen nunca llegarán a tu sistema de + destino. (Se reportó que el publicador JSON de ODK Aggregate envía valores + duplicados. Si eso pasa y tu run falla por "valores duplicados" en un campo + único concreto, puedes ignorar el run en OpenFn sin problema). + +Puedes editar los datos de tu sistema de destino desde la interfaz de ese +sistema. Muchas herramientas que actúan como `sources` (como ODK) no facilitan +editar y volver a enviar datos. Puedes usar OpenFn para editar los datos de +origen antes de reintentar el attempt. + +### Mensajes de error comunes {#common-error-messages} + +Estos son los mensajes de error más comunes, con sus explicaciones: + +```sh +DUPLICATE_VALUE: duplicate value found: ODK_uuid__c duplicates value on record with id: a0524000005wNw0 +The insert is blocked because you are attempting to create a new record with a +unique field with the same value as an existing record. +``` + +```sh +Required value missing +``` + +```sh +ExternalId not found +``` + +```sh +{ INVALID_FIELD_FOR_INSERT_UPDATE: Unable to create/update fields: Contact__c. +Please check the security settings of this field and verify that it is +read/write for your profile or permission set. } +``` + +Este último puede aparecer si una relación maestro-detalle en Salesforce no está +configurada como reparentable y el usuario intenta ejecutar un upsert. diff --git a/i18n/es/docusaurus-plugin-content-docs/current/tutorials/commcare-to-db.md b/i18n/es/docusaurus-plugin-content-docs/current/tutorials/commcare-to-db.md new file mode 100644 index 000000000000..0152bbf4105d --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/tutorials/commcare-to-db.md @@ -0,0 +1,195 @@ +--- +sidebar_label: De CommCare a PostgreSQL +title: Sincroniza los envíos de formularios de CommCare con una base de datos PostgreSQL +translation_source_hash: 9b51cc0ee8f81c1b020b68b3d5226cbfe7a60eab +translation_review_status: machine +--- + +**Antes de empezar este tutorial, asegúrate de lo siguiente:** + +- Te registraste en [OpenFn.org](http://openfn.org) (¡toma menos de un minuto!). +- Revisaste nuestro glosario y conoces la terminología básica de OpenFn y de las + API. Para empezar, consulta estas páginas: + - [Conceptos de OpenFn](/get-started/terminology.md) + - [Glosario de integración de datos](/get-started/glossary.md) +- Tienes una aplicación de CommCare con al menos un formulario configurado. Este + es tu sistema de origen. +- Tienes una base de datos PostgreSQL configurada. Este es tu sistema de + destino. + +**Si no tienes una aplicación de CommCare ni una base de datos PostgreSQL +configuradas, también puedes seguir el tutorial con la solución ya preparada. +Encontrarás todo en estos enlaces:** + +1. [Documento de especificaciones de mapeo](https://docs.google.com/spreadsheets/d/1pi_oxImakhtaCCCIENkjTPZeuyWhpFEcNmH7hfvTBgo/edit?usp=sharing) +2. Aplicación de CommCare para descargar: + - Nombre de usuario: testuser + - Contraseña: 123 + +![install_cc_app](/img/install_cc_app.webp) + +3. [Reporte público con los registros de la base de datos PostgreSQL](https://analytics.openfn.org/public/question/095449a9-5696-463c-a4fb-24614c9f08a5) + +## Primeros pasos {#getting-started} + +En esta guía vamos a configurar una **sincronización automática de datos entre +CommCare y una base de datos PostgreSQL**. Sincronizaremos los envíos de una +aplicación de CommCare llamada `Maternal and Newborn Health`, que tiene el +formulario `Register a New Patient`. + +:::tip + +Cada vez que un usuario de CommCare registre a un paciente nuevo, sus datos se +sincronizarán automáticamente con una base de datos PostgreSQL ya configurada. +Así podrás monitorear y analizar en tiempo real los datos recolectados en campo. +Por ejemplo, puedes conectar rápidamente esta base de datos a un tablero que +muestre datos agregados de los pacientes registrados. + +::: + +![cc-postgres](/img/cc-postgres.webp) + +**Esta integración se divide en dos partes:** + +1. Llevar los datos de tu sistema de origen a OpenFn para disparar tu workflow +2. Transformar estos datos y cargarlos en tu sistema de destino + +¡Empecemos! + +## Obtener datos de CommCare {#getting-data-from-commcare} + +**Hay dos formas de llevar los envíos de formularios de CommCare a OpenFn.** + +### Opción 1: un webhook que reenvía casos o formularios de CommCare a OpenFn en tiempo real con un servicio REST {#option-1-webhook-to-forward-cases-andor-forms-in-real-time-from-commcare-to-openfn-using-rest-service} + +CommCareHQ tiene una función nativa de reenvío de datos: un servicio +webhook/REST que puedes apuntar al destino que elijas (es decir, tu workflow de +OpenFn). Con un webhook configurado, todos los formularios que se envían en +CommCare se **_reenvían automáticamente_** al endpoint indicado, como tu +workflow de OpenFn. Una vez configurado, el reenvío funciona solo, **_en tiempo +real para todos los formularios y casos_**. Aprende a configurar un webhook +[aquí](/adaptors/commcare#webhook-or-data-forwarding-setup-commcare-to-openfn). + +![option1](/img/option1.webp) + +### Opción 2: extraer datos de CommCare con la API REST {#option-2-extracting-commcare-data-via-the-rest-api} + +CommCare ofrece una +[API REST](https://confluence.dimagi.com/display/commcarepublic/List+Forms) +robusta para extraer y cargar datos. Esta segunda opción consiste en configurar +un step en OpenFn que obtenga los envíos de CommCare con una solicitud HTTP +`GET`, con parámetros para filtrar la consulta. Para acceder a la API de +CommCare necesitas un plan de pago de CommCare. + +La principal ventaja del webhook es que tus datos llegan al sistema de destino +en tiempo real. Aun así, la API List Forms también tiene ventajas: permite +extraer datos en lote de forma programada, por ejemplo, para sincronizar datos +históricos el día 30 de cada mes. La opción que elijas depende de las +necesidades de tu organización. + +### Configura un workflow con la opción 1 {#set-up-a-workflow-using-option-1} + +1. **Abre un proyecto existente y crea un workflow nuevo** + +![create_new_workflow](/img/create-new-workflow.gif) + +2. **Crea un trigger "Webhook" nuevo para programar este job de extracción.** + +![create_trigger](/img/create_trigger.gif) + +Asegúrate de copiar en CommCare la URL del webhook de tu workflow de OpenFn. +Cada formulario que se envíe en CommCare llegará automáticamente a OpenFn y +disparará tu nuevo workflow. + +## Transformar y cargar los datos de CommCare en una base de datos PostgreSQL {#transforming-and-loading-commcare-data-to-a-postgresql-database} + +1. **Necesitas una base de datos configurada y un nombre de usuario para que + OpenFn pueda leer y escribir datos en las tablas de destino.** Para esta + demostración, configuramos la base de datos + [así](https://docs.google.com/spreadsheets/d/1pi_oxImakhtaCCCIENkjTPZeuyWhpFEcNmH7hfvTBgo/edit?usp=sharing) + para guardar los datos del formulario de CommCare. Consulta + [esta página](/design/mapping-specs.md) para aprender a crear tu propio + `mapping specification document` y mapear los elementos de datos que se van a + intercambiar. + +![db_config](/img/db_config.webp) + +2. **Crea un step nuevo con el adaptor `postgresql` para cargar los datos de + CommCare en tu base de datos de destino.** + +![configure_job_postgres](/img/create-job.gif) + +3. **Crea una credencial de PostgreSQL, que el step usará para autenticarse con + la base de datos.** + +![add_credential_postgres](/img/postgresql-cred.gif) + +4. **Escribe el step:** en este step usaremos la operación upsert para insertar + o actualizar registros en la tabla de destino `patient`, con `patient_id` + como clave primaria. Un `upsert` actualiza una fila si el valor indicado ya + existe en la tabla, y si no existe, inserta una fila nueva. + +```js +upsert('patient', 'ON CONSTRAINT patient_pk', { + patient_id: dataValue('data.patient_name'), + patient_name: dataValue('data.patient_name'), + village_name: dataValue('data.village_name'), + last_menstrual_period: dataValue('data.last_menstrual_period'), + expected_delivery_date: dataValue('data.expected_delivery_date'), + children_alive: dataValue('data.children_alive'), + living_children: dataValue('data.living_children'), + feeling_sick: dataValue('data.feeling_sick'), + total_children: dataValue('data.Total_children'), + risk_level: dataValue('data.Risk_level'), +}); +``` + +Puedes modificar este código para adaptarlo a tu configuración de CommCare y de +la base de datos, según tus especificaciones de mapeo. + +![create-job](/img/create_job_db.gif) + +## ¡Hora de probar! {#time-to-test} + +1. Envía un formulario en CommCare. +2. Si activaste el reenvío de datos, tu workflow debería dispararse + automáticamente. +3. Si no activaste el reenvío de datos y en su lugar configuraste un step FETCH, + ejecuta el step (revisa que las fechas `received_on_start` y + `received_on_start` del FETCH sean las correctas). +4. Ejecuta el step FETCH. Si funciona, el step "Load to DB" debería ejecutarse + automáticamente. +5. Revisa el `History` y comprueba que la work order se completó con éxito. + +![activity_history_final](/img/activity_history_success.webp) + +:::info + +**Qué hacer si tu run falla:** + +1. Abre el run para revisar el registro de errores. +2. Ajusta el step para resolver el problema y vuelve a ejecutarlo las veces que + haga falta con el botón "rerun" en `History` o con el botón "Re-run from + here" en el `Inspector`. +3. Consulta la página de + [errores comunes de PostgreSQL](/adaptors/postgresql/#common-errors) para ver + más detalles. + +::: + +4. **Por último, actualiza tu base de datos y revisa los datos del nuevo + envío.** + +![metabase](/img/metabase.webp) + +Aunque esta guía es específica para bases de datos PostgreSQL, en general puedes +seguir los mismos pasos con otros tipos de bases de datos (por ejemplo, MS SQL o +MySQL): solo tienes que usar otro adaptor en la configuración del step. + +**Otros recursos que puedes consultar:** + +1. La biblioteca de jobs de OpenFn +2. Las páginas de "App" de CommCare y Postgres en la documentación de OpenFn + +**¿Tienes preguntas, comentarios o ideas nuevas de configuración? Escríbenos en +el foro de la [comunidad de OpenFn](https://community.openfn.org/).** diff --git a/i18n/es/docusaurus-plugin-content-docs/current/tutorials/http-to-googlesheets.md b/i18n/es/docusaurus-plugin-content-docs/current/tutorials/http-to-googlesheets.md new file mode 100644 index 000000000000..0e9305e228ed --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/tutorials/http-to-googlesheets.md @@ -0,0 +1,177 @@ +--- +sidebar_label: De HTTP a GoogleSheets +title: Workflow de HTTP a GoogleSheets +translation_source_hash: dda6b88cd13c6a33fde0ac1765e545b6c743d03c +translation_review_status: machine +--- + +# Crea un workflow que conecte una API REST con Google Sheets + +En este tutorial te mostramos cómo crear un workflow sencillo de OpenFn que +automatiza la sincronización de datos entre una API REST y Google Sheets, con +los [adaptors](/adaptors) `http` y `GoogleSheets`. + +## Video explicativo {#video-walkthrough} + +Mira el video y sigue los pasos que aparecen abajo. + + + +## Antes de empezar {#before-you-start} + +Antes de comenzar, damos por hecho que revisaste lo siguiente: + +- Revisaste nuestro glosario y conoces los conceptos básicos de OpenFn y de las + API. Para empezar, consulta estas páginas: + - [Conceptos de OpenFn](/get-started/terminology.md) + - [Glosario de integración de datos](/get-started/glossary.md) +- Tienes una cuenta de Google. La usaremos para crear una credencial que + autorice el acceso a Google Sheets. +- Tienes acceso a un proyecto de OpenFn (en una + [aplicación OpenFn v2](https://github.com/OpenFn/lightning) instalada + localmente o en [app.openfn.org](https://app.openfn.org)). + +## Primeros pasos {#getting-started} + +En esta guía vamos a configurar un workflow que **sincronice automáticamente +datos de `user` desde una API REST web y los importe a una GoogleSheet**. + +**Esta integración se divide en dos partes:** + +1. Obtener datos de la API REST (la aplicación de "origen") +2. Transformar e importar estos datos a una tabla de tu GoogleSheet (la + aplicación de "destino") + +¡Empecemos! + +## 1: Crea un workflow nuevo {#1-create-a-new-workflow} + +Para crear un workflow nuevo en tu proyecto: + +1. Ve a la página `project dashboard`. +2. Haz clic en el botón `Create new workflow`. +3. Ponle a tu workflow un `Name` descriptivo (por ejemplo, `Sync Users List`). +4. Elige tu [trigger](/build/triggers.md). +5. Edita tu primer [step](/build/steps/steps.md). + +## 2. Configura tu primer step para obtener datos de la API REST {#2-configure-your-first-step-to-get-data-from-the-rest-api} + +[JSONPlaceholder](https://jsonplaceholder.typicode.com/users) ofrece una API +falsa y gratuita para hacer pruebas y prototipos. Usaremos la +[API REST de usuarios](https://jsonplaceholder.typicode.com/users) para extraer +datos de usuarios. Para eso, configuraremos un step en OpenFn que obtenga esos +datos con una solicitud HTTP `GET`. Haz clic en tu primer step para configurarlo +con estas opciones: + +- Name `Fetch Users` +- Adaptor `http` +- Version: `6.0.0` +- Credentials (opcional: credencial "Raw JSON") - + `{ "baseUrl": "https://jsonplaceholder.typicode.com/"}` +- Código del job: si configuraste la credencial "Raw JSON" con jsonplaceholder + como baseURL, agrega la operación `get("users")` en el bloque de código. + +:::tip ¿Necesitas ayuda para escribir el código del job? + +Consulta la documentación sobre el +[adaptor "http"](/adaptors/packages/http-readme), sobre +[cómo configurar steps](/build/steps/steps.md) y sobre +[cómo escribir jobs](/jobs/job-writing-guide.md). + +::: + +**Cuando termines de configurar y escribir tu step, ¡guárdalo y ejecútalo!** + +- Consulta la [sección de workflows](/build/workflows.md) para ver más + orientación sobre cómo crear y ejecutar workflows. + +**Revisa el panel `Output & Log` para ver si tu run se completó con éxito.** Si +fue así, deberías ver: + +- El estado `success` +- La pestaña Log termina con `Run complete with status: success` +- La pestaña Input muestra `{}` +- La pestaña Output muestra `{ data: [ {...}]}` + +## 3. Configura otro step para transformar los datos e importarlos a tu GoogleSheet {#3-configure-another-step-to-transform-the-data--import-your-googlesheet} + +Crea una `Credential` nueva de Googlesheet con el correo de tu cuenta de Google. +(Asegúrate de que este usuario de Google tenga permiso de edición en la +GoogleSheet que quieres integrar). + +:::info ¿No ves la opción de credencial de GoogleSheets? + +Si el superusuario de tu instancia no configuró un cliente OAuth global, quizás +tengas que configurar uno tú. Consulta +[los clientes OAuth](/manage-projects/oauth.md#oauth-clients) y +[los detalles de un cliente de GoogleSheet](/adaptors/googlesheets#permissions-scopes). + +::: + +Para esta demostración, configuramos la Googlesheet +[así](https://docs.google.com/spreadsheets/d/1gT4cpHSDQp8A_JIX_5lqTLTwV0xBo_u8u3ZNWALmCLc/edit?usp=sharing) +para guardar los datos de `users`. + +Crea un step nuevo con el adaptor `googlesheets` para cargar los datos de los +usuarios en tu GoogleSheet de destino. Configura el step con estas opciones: + +- Name `Sync Users` +- Adaptor `googlesheets` +- Version: `2.2.2` +- Credentials: crea una credencial `GoogleSheet OAuth` nueva y guárdala +- Operaciones del step: en este job usaremos la operación `appendValues()` para + agregar un array de filas a la hoja de cálculo. Asegúrate de cambiar el + `spreadsheetId` por el ID de tu hoja de cálculo. + + ```js + // Prepara el array con los datos de los usuarios + fn(state => { + const users = state.data.map( + ({ id, name, username, address, phone, website, company }) => [ + id, + name, + username, + address.city, + phone, + website, + company.name, + ] + ); + + return { ...state, users }; + }); + + // Agrega los datos de los usuarios a la GoogleSheet + appendValues({ + spreadsheetId: '1gT4cpHSDQp8A_JIX_5lqTLTwV0xBo_u8u3ZNWALmCLc', + range: 'users!A1:G1', + values: state => state.users, + }); + ``` + +- Input - `Final output of Fetch Users` + +Si ya ejecutaste el step `Fetch Users`, tendrás una entrada inicial para probar +el step `Sync Users`. Selecciona la entrada en el panel Input y haz clic en +`Create New Work Order` para ejecutar este step. + +## 4. ¡Hora de probar! {#4-time-to-test} + +1. Selecciona el step `Fetch Users` y ábrelo en el Inspector. +2. Crea una entrada nueva vacía `{}`. +3. Haz clic en `Create New Work Order` para ejecutar el step. +4. Revisa los resultados en el panel `Output & Logs` y comprueba que los dos + steps terminaron con el estado `success`. +5. Por último, revisa tu hoja de cálculo para ver los datos de usuarios + sincronizados. + +¿Te aparecen errores o no sabes cómo seguir? Consulta la documentación sobre +[workflows](/build/workflows.md) o sobre +[solución de problemas](/monitor-history/troubleshooting.md). + +:::tip ¿No puedes avanzar? ¿Tienes preguntas? + +Recuerda [ver el video](#video-walkthrough) o publicar en la +[comunidad](https://community.openfn.org) para pedir ayuda. + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/tutorials/kobo-to-dhis2.md b/i18n/es/docusaurus-plugin-content-docs/current/tutorials/kobo-to-dhis2.md new file mode 100644 index 000000000000..22552a0d93c6 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/tutorials/kobo-to-dhis2.md @@ -0,0 +1,226 @@ +--- +sidebar_label: De Kobo a DHIS2 +title: Workflow de reportes de Kobo a DHIS2 +slug: /kobo-to-dhis2 +translation_source_hash: b39aafc3fe9d4c25cde774798c8f9cfb06f0b082 +translation_review_status: machine +--- + +# Crea un workflow que automatice los reportes entre KoboToolbox y DHIS2 + +En este tutorial te mostramos cómo crear un workflow sencillo de OpenFn que +automatiza los reportes entre [KoboToolbox](https://www.kobotoolbox.org/) (una +aplicación móvil de recolección de datos) y [DHIS2](https://dhis2.org) (un +sistema de información de salud muy usado para reportar datos agregados sobre +indicadores clave), con los [adaptors](/adaptors) `kobotoolbox` y `dhis2`. + +### Video explicativo {#video-walkthrough} + +:::tip Tutorial de introducción a workflows y History + +Mira este +[tutorial de introducción a workflows y History](https://youtu.be/hae8eM0iYnM?si=LGbv1TK0W9L9y12u) +para que te guíe en la configuración de este workflow. + +::: + +### Resumen del workflow {#workflow-overview} + +Este workflow de OpenFn tendrá 3 steps: + +1. Obtener los envíos de formularios de Kobotoolbox +2. Contar cuántos valores `OPV0_dose_given` hay en los envíos, para saber + cuántos beneficiarios recibieron la vacuna OPV0 +3. Importar los resultados agregados a DHIS2 para reportar el número de dosis + registradas esa semana + +### Requisitos previos {#prerequisites} + +- Tienes un proyecto de OpenFn. +- Tienes una cuenta de KoboToolbox y un formulario para sincronizar (más abajo + encontrarás credenciales de demostración). +- Tienes los datos de acceso a una instancia de DHIS2 (más abajo encontrarás los + de la instancia "play" de DHIS2). + +### Step 1: Obtener los envíos del formulario de Kobo {#step-1-get-kobo-form-submission} + +Crea el primer step en el Canvas del workflow. + +- Name: `Get Kobo Form Submission` +- Adaptor: `kobotoolbox` +- Version: `latest` +- Credential: ver abajo + +Este step usa el adaptor kobotoolbox con la siguiente configuración de +credencial: + +```json +{ + "baseURL": "https://kf.kobotoolbox.org", + "username": "openfn_demo", + "password": "openfn_demo", + "apiVersion": "v2" +} +``` + +En este step queremos obtener los envíos del formulario de demostración con el +ID `aBpweTNdaGJQFb5EBBwUeo`. Para eso, abre el +[editor del Inspector](/build/steps/step-editor.md) y agrega el siguiente código +del job: + +```javascript +// Step 1: obtiene los envíos del formulario de Kobotoolbox +getSubmissions({ formId: 'aBpweTNdaGJQFb5EBBwUeo' }); +``` + +:::tip ¿Necesitas ayuda para escribir el código del job? + +Consulta la documentación sobre el +[adaptor "kobotoolbox"](/adaptors/kobotoolbox), sobre +[cómo configurar steps](/build/steps/steps.md) y sobre +[cómo escribir jobs](/jobs/job-writing-guide.md). + +::: + +#### Explicación {#explanation} + +- `getSubmissions`: obtiene los envíos de formularios. +- `{ formId: "aBpweTNdaGJQFb5EBBwUeo" }`: indica el ID del formulario del que se + obtienen los envíos. + +#### Prueba {#testing} + +Crea una entrada vacía `{}` y haz clic en el botón `Create New Work Order` para +ejecutar el workflow. Consulta [la documentación](/build/workflows.md) para +saber más sobre cómo ejecutar workflows manualmente. + +El `output` esperado debería contener 17 registros en `state.data.results`. + +### Step 2: Contar las dosis de OPV aplicadas {#step-2-count-opv-dose-given} + +Crea un segundo step después de `Get Kobo Form Submission` así: + +- Name: `Count OPV Dose Given` +- Adaptor: `common` (se usa cuando quieres agregar funciones de JavaScript + personalizadas) +- Version: `latest` +- Credential: no hace falta + +En este step vamos a contar todos los registros con `"OPV0_dose_given": "yes"`. +Para agregar esta lógica, abre el [Inspector](/build/steps/step-editor.md) y +agrega el siguiente código del job en el editor: + +```javascript +// Filtra y cuenta las dosis de OPV aplicadas +fn(state => { + const opvDosesGivenCount = state.data.results.filter( + r => r['OPV0_dose_given'] === 'yes' + ).length; + + return { ...state, opvDosesGivenCount }; +}); +``` + +:::tip ¿Necesitas ayuda para escribir el código del job o para cambiar esta +lógica? + +Consulta la documentación sobre el +[adaptor "common"](/adaptors/packages/common-docs), sobre +[cómo configurar steps](/build/steps/steps.md) y sobre +[cómo escribir jobs](/jobs/job-writing-guide.md). + +::: + +#### Explicación {#explanation-1} + +- `fn`: una función de OpenFn que da más flexibilidad al escribir jobs. Te + permite hacer algo con el state y devolver los datos transformados al state. +- `opvDosesGivenCount`: cuenta cuántas veces aparece "yes" en el campo + `OPV0_dose_given`. + +#### Prueba {#testing-1} + +Selecciona el primer step, `Get Kobo Form Submission`, y haz clic en +`Create New Work Order` con una entrada vacía (consulta la +[documentación de workflows](/build/workflows.md) si necesitas ayuda para +ejecutar y probar steps). Los dos steps deberían ejecutarse con éxito y en el +state final deberías ver que se agregó `opvDosesGivenCount: 3`. + +### Step 3: Mapear y cargar en DHIS2 {#step-3-map-and-load-to-dhis2} + +Crea un tercer step después de `Count OPV Dose Given` así: + +- Name: `Map and Load to DHIS2` +- Adaptor: `dhis2` +- Version: `v4.0.3` +- Credential: una credencial `dhis2` nueva con la siguiente configuración + +```json +{ + "hostUrl": "https://play.dhis2.org/dev", + "username": "admin", + "password": "district" +} +``` + +En este step queremos agregar la lógica para importar `dataValues` a DHIS2 y así +"reportar" el total de dosis de la vacuna OPV0 que calculamos en el step 2. + +Para eso, abre el [Inspector](/build/steps/step-editor.md) y agrega el siguiente +código del job en el editor: + +```javascript +// Importa a DHIS2 +create('dataValueSets', state => ({ + dataSet: 'BfMAe6Itzgt', // Child Health + period: '202402', // Feb 2024 + orgUnit: 'DiszpKrYNg8', // Ngelehun CHC + dataValues: [ + { + categoryOptionCombo: 'Prlt0C1RF0s', //Fixed <1yr + dataElement: 'x3Do5e7g4Qo', // OPV0 doses given + value: state.opvDosesGivenCount, //# of OPV0 doses given + }, + ], +})); +``` + +:::tip ¿Necesitas ayuda para escribir el código del job o para cambiar esta +lógica? + +Consulta la documentación sobre el [adaptor "dhis2"](/adaptors/dhis2), sobre +[cómo configurar steps](/build/steps/steps.md) y sobre +[cómo escribir jobs](/jobs/job-writing-guide.md). + +::: + +#### Explicación {#explanation-2} + +- `create('dataValueSets', {...})`: esta función de OpenFn crea un datavalueset + nuevo en DHIS2. +- `dataSet`, `completeDate`, `period`, `orgUnit`: los detalles del datavalueset. +- `dataValues`: un array con los elementos de datos y sus valores. + +#### Prueba {#testing-2} + +Guarda los cambios, ve al primer step (Get Kobo Form Submission), crea una +entrada vacía `{}` y haz clic en el botón `Create New Work Order` para ejecutar +el workflow. Todos los steps deberían ejecutarse con éxito y deberías ver +actualizado `OPV0 doses given` en DHIS2. Consulta la +[documentación de workflows](/build/workflows.md) si necesitas ayuda para +ejecutar o probar workflows. + +### Conclusión {#conclusion} + +¡Felicitaciones! Creaste un workflow de OpenFn que automatiza todo el proceso: +obtiene los envíos de formularios de Kobotoolbox, calcula el total de dosis de +OPV aplicadas a los beneficiarios y reporta ese total a DHIS2 como `dataValues`. + +:::tip ¿No puedes avanzar? ¿Tienes preguntas? + +Mira este +[tutorial de introducción a workflows y History](https://youtu.be/hae8eM0iYnM?si=LGbv1TK0W9L9y12u) +o publica tus preguntas en la [comunidad](https://community.openfn.org) para +recibir ayuda. + +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/tutorials/tutorial.md b/i18n/es/docusaurus-plugin-content-docs/current/tutorials/tutorial.md new file mode 100644 index 000000000000..de69be9ace0f --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/tutorials/tutorial.md @@ -0,0 +1,43 @@ +--- +title: Tutorial +sidebar_label: Guía rápida de workflows +translation_source_hash: ee5eee9bd2e17a736919bba3e005069daac0a44f +translation_review_status: machine +--- + +# Tutorial: crea tu primer workflow + +# Guía rápida: crea tu primer workflow + +1. Ve a tu proyecto de OpenFn > `Workflows`. +2. Crea un [workflow](/build/workflows.md) nuevo. +3. Elige el [tipo de trigger](/build/triggers.md): Webhook Event (para + integraciones en tiempo real) o Cron Expression (para integraciones + programadas). +4. Ponle nombre a tu primer `Step` (por ejemplo, "Import form submission") y + ábrelo para elegir el [adaptor](/adaptors), la `Version` del adaptor y la + [credencial](/build/credentials.md). +5. Haz clic en el botón de código `` para abrir el + [Inspector](/build/steps/step-editor.md) y agrega el código del job en el + panel `Editor` para definir la lógica de negocio o las reglas de + transformación de este workflow. +6. En el panel `Input` de la izquierda, agrega una entrada personalizada (por + ejemplo, el payload de una solicitud de webhook) o simplemente agrega llaves + vacías (`{}`) para ejecutar un workflow con un trigger cron. Consulta la + [documentación de workflows](/build/workflows.md) si necesitas ayuda para + ejecutar y probar workflows. +7. Si el step funciona, vuelve a la vista del Canvas y haz clic en el ícono `+` + para agregar un segundo step. +8. Si quieres definir condiciones para decidir si este segundo step se ejecuta y + cuándo, actualiza la [condición del path](/build/paths.md). +9. Luego repite los pasos 3 a 6 para terminar de configurar este step, hasta + completar el workflow. + +:::tip + +Mira el video y la documentación de la +[página de workflows](/build/workflows.md) en la sección `Build` para obtener +ayuda detallada, o haz tus preguntas en la +[comunidad](https://community.openfn.org). + +::: diff --git a/i18n/es/docusaurus-theme-classic/footer.json b/i18n/es/docusaurus-theme-classic/footer.json new file mode 100644 index 000000000000..0279f041812f --- /dev/null +++ b/i18n/es/docusaurus-theme-classic/footer.json @@ -0,0 +1,42 @@ +{ + "link.title.This Site": { + "message": "Este sitio", + "description": "The title of the footer links column with title=This Site in the footer" + }, + "link.title.Community": { + "message": "Comunidad", + "description": "The title of the footer links column with title=Community in the footer" + }, + "link.title.More": { + "message": "Más", + "description": "The title of the footer links column with title=More in the footer" + }, + "link.item.label.Articles": { + "message": "Artículos", + "description": "The label of footer link with label=Articles linking to articles" + }, + "link.item.label.Adaptors": { + "message": "Adaptors", + "description": "The label of footer link with label=Adaptors linking to adaptors" + }, + "link.item.label.Forum": { + "message": "Foro", + "description": "The label of footer link with label=Forum linking to https://community.openfn.org" + }, + "link.item.label.Stack Overflow": { + "message": "Stack Overflow", + "description": "The label of footer link with label=Stack Overflow linking to https://stackoverflow.com/questions/tagged/openfn" + }, + "link.item.label.Twitter": { + "message": "Twitter", + "description": "The label of footer link with label=Twitter linking to https://twitter.com/openfn" + }, + "link.item.label.OpenFn.org": { + "message": "OpenFn.org", + "description": "The label of footer link with label=OpenFn.org linking to https://www.openfn.org" + }, + "link.item.label.GitHub": { + "message": "GitHub", + "description": "The label of footer link with label=GitHub linking to https://github.com/openfn" + } +} diff --git a/i18n/es/docusaurus-theme-classic/navbar.json b/i18n/es/docusaurus-theme-classic/navbar.json new file mode 100644 index 000000000000..f56684752de1 --- /dev/null +++ b/i18n/es/docusaurus-theme-classic/navbar.json @@ -0,0 +1,22 @@ +{ + "title": { + "message": "OpenFn", + "description": "The title in the navbar" + }, + "logo.alt": { + "message": "OpenFn", + "description": "The alt text of navbar logo" + }, + "item.label.Docs": { + "message": "Documentación", + "description": "Navbar item with label Docs" + }, + "item.label.Adaptors": { + "message": "Adaptors", + "description": "Navbar item with label Adaptors" + }, + "item.label.Articles": { + "message": "Artículos", + "description": "Navbar item with label Articles" + } +} diff --git a/sidebars-adaptors.js b/sidebars-adaptors.js index fe4b96418fd7..b94e3b8b5614 100644 --- a/sidebars-adaptors.js +++ b/sidebars-adaptors.js @@ -25,28 +25,38 @@ if ( return r; }, Object.create(null)); + // Every adaptor repeats the same item labels ('Functions', 'Overview', ...). + // Docusaurus derives a sidebar item's translation key from `key ?? label`, + // so without an explicit `key` those labels collide and the build throws + // `Multiple docs sidebar items produce the same translation key` for any + // non-default locale. Namespacing each key by adaptor keeps them unique. const items = adaptors.sort().map(a => { const base = { type: 'category', label: a.name, + key: a.name, items: [ { type: 'doc', label: 'Functions', + key: `${a.name}-functions`, id: a.docsId, }, { type: 'doc', label: 'Configuration', + key: `${a.name}-configuration`, id: a.configurationSchemaId, }, groupedJobs[a.name] && groupedJobs[a.name].length > 0 ? { type: 'category', label: 'Examples', + key: `${a.name}-examples`, items: groupedJobs[a.name].map(j => ({ type: 'doc', label: j.name, + key: `library/${j.id}`, id: `library/${j.id}`, })), } @@ -54,11 +64,13 @@ if ( { type: 'doc', label: 'Changelog', + key: `${a.name}-changelog`, id: a.changelogId, }, { type: 'doc', label: 'README.md', + key: `${a.name}-readme`, id: a.readmeId, }, ], @@ -70,6 +82,7 @@ if ( base.items.unshift({ type: 'doc', label: 'Overview', + key: `${a.name}-overview`, id: a.name, }); } @@ -88,7 +101,7 @@ if ( const extras = overviews .filter(id => !adaptors.map(a => `${a.name}`).includes(id)) - .map(id => ({ type: 'doc', id, label: id })); + .map(id => ({ type: 'doc', id, label: id, key: id })); list = [...items, ...extras].sort((a, b) => a.label.localeCompare(b.label)); } else { diff --git a/src/components/UntranslatedNotice.js b/src/components/UntranslatedNotice.js new file mode 100644 index 000000000000..56da9e43dce5 --- /dev/null +++ b/src/components/UntranslatedNotice.js @@ -0,0 +1,27 @@ +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Translate from '@docusaurus/Translate'; + +// Translated files live under i18n/; any other source is the English fallback. +export function isUntranslated(source) { + return !source.startsWith('@site/i18n/'); +} + +// Shown on a non-English locale when the page has no translation and +// Docusaurus has fallen back to the English source file. +export default function UntranslatedNotice({ source }) { + const { i18n } = useDocusaurusContext(); + if (i18n.currentLocale === i18n.defaultLocale) return null; + if (!isUntranslated(source)) return null; + return ( +
+ + + This page isn't available in your language, so we're showing it in + English. + +
+ ); +} diff --git a/src/pages/index.js b/src/pages/index.js index 4ef9e65e6de7..c0c5bfc2f75c 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -2,7 +2,7 @@ import React, { useCallback } from 'react'; import clsx from 'clsx'; import Layout from '@theme/Layout'; import Link from '@docusaurus/Link'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Translate, { translate } from '@docusaurus/Translate'; import useBaseUrl from '@docusaurus/useBaseUrl'; import Particles from 'react-particles'; import { loadFull } from 'tsparticles'; @@ -10,77 +10,104 @@ import styles from './styles.module.css'; const highlights = [ { - title: 'Job Writing Guide', + title: translate({ + id: 'homepage.highlights.jobWritingGuide.title', + message: 'Job Writing Guide', + }), link: 'documentation/jobs/job-writing-guide', - description: 'Writing a job for OpenFn? Start here', + description: translate({ + id: 'homepage.highlights.jobWritingGuide.description', + message: 'Writing a job for OpenFn? Start here', + }), }, { - title: 'CLI Usage Examples', + title: translate({ + id: 'homepage.highlights.cliUsage.title', + message: 'CLI Usage Examples', + }), link: 'documentation/cli-usage', - description: 'See what the CLI can do at a glance', + description: translate({ + id: 'homepage.highlights.cliUsage.description', + message: 'See what the CLI can do at a glance', + }), }, { - title: 'JavaScript Tips & Tricks', + title: translate({ + id: 'homepage.highlights.javascriptTips.title', + message: 'JavaScript Tips & Tricks', + }), link: 'documentation/cli-usage', - description: 'Level up your code', + description: translate({ + id: 'homepage.highlights.javascriptTips.description', + message: 'Level up your code', + }), }, ]; const features = [ { - title: 'Docs', + title: translate({ id: 'homepage.features.docs.title', message: 'Docs' }), link: 'documentation', imageUrl: 'img/undraw_Code_review_re_woeb.svg', description: ( - <> + Documentation on all aspects of OpenFn, the leading digital public good for workflow automation. - + ), }, { - title: 'Adaptors', + title: translate({ + id: 'homepage.features.adaptors.title', + message: 'Adaptors', + }), link: 'adaptors', imageUrl: 'img/undraw_pair_programming_njlp.svg', description: ( - <> + Searchable and browseable adaptors docs, examples, changelogs, and overviews for connecting the world's most common DPGs. - + ), }, { - title: 'Articles', + title: translate({ + id: 'homepage.features.articles.title', + message: 'Articles', + }), link: 'articles', imageUrl: 'img/undraw_Portfolio_update_re_jqnp.svg', description: ( - <> + How to prepare for data integration? How to structure external IDs? How to... - + ), }, { - title: 'Blog', + title: translate({ id: 'homepage.features.blog.title', message: 'Blog' }), link: 'https://openfn.org/blog', imageUrl: 'img/undraw_reading_time_gvg0.svg', description: ( - <> + We help the world's most promising social impact interventions achieve scale through automation, data integration, and interoperability. These are their stories. - + ), }, { - title: 'Enterprise', + title: translate({ + id: 'homepage.features.enterprise.title', + message: 'Enterprise', + }), link: 'https://www.openfn.org', imageUrl: 'img/undraw_secure_server_s9u8.svg', description: ( - <> + Check out the enterprise-grade OpenFn integration-platform-as-a-service (iPaaS), offering free-forever plans and affordable pathways to scale. - + ), }, ]; @@ -109,9 +136,6 @@ function Feature({ imageUrl, title, description, link }) { } function Home() { - const context = useDocusaurusContext(); - const { siteConfig = {} } = context; - const particlesInit = useCallback(async engine => { await loadFull(engine); }, []); @@ -235,7 +259,13 @@ function Home() { }; return ( - +
-

OpenFn Documentation

-

{siteConfig.tagline}

+

+ OpenFn Documentation +

+

+ + The leading digital public good for workflow automation, OpenFn + makes ICT4D more efficient. + +

- Get Started + Get Started
@@ -271,13 +308,22 @@ function Home() { Newsletter -

Newsletter

+

+ + Newsletter + +

- Never miss a story from us, subscribe to our newsletter - here. + + Never miss a story from us, subscribe to our newsletter + here. +

- Subscribe + + Subscribe +
@@ -312,12 +363,16 @@ function Home() { )}
-

✨Documentation Highlights✨

+

+ + ✨Documentation Highlights✨ + +

{highlights.map(h => (

- {h.title} + {h.title}

{h.description}

diff --git a/src/theme/BlogPostItem/index.js b/src/theme/BlogPostItem/index.js new file mode 100644 index 000000000000..43de4aff5f44 --- /dev/null +++ b/src/theme/BlogPostItem/index.js @@ -0,0 +1,13 @@ +import BlogPostItem from '@theme-original/BlogPostItem'; +import { useBlogPost } from '@docusaurus/plugin-content-blog/client'; +import UntranslatedNotice from '@site/src/components/UntranslatedNotice'; + +export default function BlogPostItemWrapper(props) { + const { metadata, isBlogPostPage } = useBlogPost(); + return ( + <> + {isBlogPostPage && } + + + ); +} diff --git a/src/theme/BlogPostItems/index.js b/src/theme/BlogPostItems/index.js new file mode 100644 index 000000000000..03ddead2dc44 --- /dev/null +++ b/src/theme/BlogPostItems/index.js @@ -0,0 +1,19 @@ +import BlogPostItems from '@theme-original/BlogPostItems'; +import UntranslatedNotice, { + isUntranslated, +} from '@site/src/components/UntranslatedNotice'; + +// Article lists: one notice at the top if any article on the page is untranslated. +export default function BlogPostItemsWrapper(props) { + const untranslated = props.items.find(({ content }) => + isUntranslated(content.metadata.source) + ); + return ( + <> + {untranslated && ( + + )} + + + ); +} diff --git a/src/theme/DocItem/Content/index.js b/src/theme/DocItem/Content/index.js new file mode 100644 index 000000000000..6bc630cd1fb6 --- /dev/null +++ b/src/theme/DocItem/Content/index.js @@ -0,0 +1,16 @@ +import Content from '@theme-original/DocItem/Content'; +import { useDoc } from '@docusaurus/plugin-content-docs/client'; +import UntranslatedNotice from '@site/src/components/UntranslatedNotice'; + +export default function ContentWrapper(props) { + const { metadata } = useDoc(); + return ( + <> + {/* v1 docs are frozen and stay English; they already have their own banner. */} + {metadata.version !== 'legacy' && ( + + )} + + + ); +} diff --git a/translation-rules.yml b/translation-rules.yml index 195e8f8c4cd7..769f94f2f23d 100644 --- a/translation-rules.yml +++ b/translation-rules.yml @@ -2,13 +2,14 @@ # # Purpose # ------- -# Locale-specific phrasing rules learned from human edits to machine -# translations. The translate skill (.agents/skills/translate/SKILL.md) loads this -# file after glossary.yml and applies every rule whose `locale` matches the -# target locale. +# Fixes learned from human review of machine translations: a phrase that must +# always be translated one way, or a rendering a reviewer rejected. The +# translate skill (.agents/skills/translate/SKILL.md) loads this file after +# glossary.yml and applies every rule whose `locale` matches the target locale. # -# Glossary terms (never translate) belong in glossary.yml, not here. This file -# is for how to translate, not what to leave alone. +# Glossary terms (never translate) belong in glossary.yml. A locale's general +# style (voice, word choice, capitals, punctuation) belongs in its house style +# guide, .agents/skills/translate/.md. This file is for single phrases. # # Humans maintain this file. Add a rule when you correct a translation in a # way that should apply to other pages too. @@ -18,13 +19,9 @@ # rules: # - locale: string # "es" or "fr" (or "*" for every locale) # kind: string # one of: -# # term - a fixed rendering for a phrase -# # register - tone/voice guidance (formal vs informal "you") -# # punctuation - spacing, quotation marks, list punctuation -# # structure - how to handle headings, admonition titles, UI labels -# # avoid - a rendering the reviewer rejected -# source: string # English phrase or pattern the rule applies to (for -# # kind: term and avoid). Omit for global rules. +# # term - a fixed rendering for a phrase +# # avoid - a rendering the reviewer rejected +# source: string # English phrase the rule applies to # target: string # required rendering (kind: term) or rejected rendering # # (kind: avoid) # instruction: string # plain-language rule the translator must follow @@ -39,20 +36,112 @@ # ------- # rules: # - locale: es -# kind: register -# instruction: Address the reader as "tú", not "usted". -# reason: Matches the informal tone of the English docs. -# added_by: someone -# added_on: 2026-01-01 -# source_pr: https://github.com/OpenFn/docs/pull/000 -# - locale: fr -# kind: punctuation -# instruction: Put a non-breaking space before ":", ";", "?" and "!". -# example_source: "Next step: add a credential." -# example_target: "Étape suivante : ajoutez un identifiant." -# reason: Standard French typography. +# kind: avoid +# source: report (a problem) +# target: informar de +# instruction: Use "reportar", not "informar de". +# reason: "Informar de" is Spain-only; the Spanish docs target es-419. # added_by: someone # added_on: 2026-01-01 # source_pr: https://github.com/OpenFn/docs/pull/000 -rules: [] +rules: + - locale: es + kind: term + source: upgrade (a plan) + target: mejorar + instruction: >- + "Upgrade" a plan or account is "mejorar", not "actualizar", which means + "update". + example_source: Upgrade to access more features. + example_target: Mejora tu plan para acceder a más funciones. + reason: '"Actualiza tu plan" reads as "update your plan".' + added_by: lmac-1 + added_on: 2026-10-01 + + - locale: es + kind: term + source: PII + target: información de identificación personal (PII) + instruction: >- + Write "información de identificación personal" and keep "PII" next to it, + so readers can match it to the English term. + example_source: Remove any data that should not be output (like PII). + example_target: >- + Elimina los datos que no deberían formar parte de la salida (como + información de identificación personal o PII). + reason: The Write Jobs pages translated PII two different ways. + added_by: lmac-1 + added_on: 2026-10-01 + + - locale: es + kind: term + source: core team + target: equipo principal + instruction: '"Core team" is "equipo principal", not "equipo central".' + example_source: Ask the OpenFn core team. + example_target: Pregunta al equipo principal de OpenFn. + reason: Every other Spanish page already uses "equipo principal". + added_by: lmac-1 + added_on: 2026-10-01 + + - locale: es + kind: avoid + source: feature (on pages about code) + target: función + instruction: >- + On pages that also talk about JavaScript or adaptor functions, translate + "feature" as "característica" or "funcionalidad", not "función". Elsewhere, + "función" is fine and usually the most natural word. + example_source: OpenFn supports all modern JavaScript features. + example_target: OpenFn admite todas las características modernas de JavaScript. + reason: >- + On developer pages "función" reads as a JavaScript function, not a + feature. On other pages "función" is what Latin American apps say. + added_by: lmac-1 + added_on: 2026-10-01 + + - locale: es + kind: avoid + source: enable / disable + target: activar / desactivar + instruction: >- + "Enable" and "disable" (a workflow, trigger, setting, or button) are + "habilitar" and "deshabilitar". Keep "activar" and "desactivar" for "turn + on", "turn off", and flipping a toggle. + example_source: >- + Workflows will run automatically when they are "enabled", i.e., when their + trigger is turned on. + example_target: >- + Los workflows se ejecutan automáticamente cuando están "habilitados", es + decir, cuando su trigger está activado. + reason: >- + The Sandboxes page used "desactivados" for triggers that the Workflows and + Triggers pages call "deshabilitados". + added_by: lmac-1 + added_on: 2026-10-02 + + - locale: es + kind: term + source: Expand to see + target: Expande para ver + instruction: >- + In dropdown summaries, "Expand to see..." is "Expande para ver...", not + "Despliega para ver...". + example_source: Expand to see the expected output + example_target: Expande para ver la salida esperada + reason: The CLI pages used "Despliega" on two pages and "Expande" on another. + added_by: lmac-1 + added_on: 2026-10-02 + + - locale: es + kind: avoid + source: run (the verb) + target: correr + instruction: >- + "Run" a job, step, workflow, or command is "ejecutar", not "correr". + example_source: A workflow is the execution plan for running several steps. + example_target: Un workflow es el plan de ejecución de varios steps. + reason: The CLI walkthrough mixed "correr" with "ejecutar" for the same verb. + added_by: lmac-1 + added_on: 2026-10-02