Skip to main content

create-tutorial

Scaffold a new Membrane API Gateway tutorial in the api-gateway repo — the numbered self-teaching YAML under distribution/tutorials/<category>/, its support files and README links, and the matching auto-discovered integration test. Use whenever the user asks to create, add, write, or scaffold a tutorial (or a tutorial step), even if they only describe the gateway behavior they want to teach and don't say the word "skill" or name the files.

来源信息

仓库
membrane/api-gateway
最近来源活动
2026年9月9日 10:53
检测到的 SKILL.md 语言
英语
星标
645
分支
182

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
create-tutorial
description
Scaffold a new Membrane API Gateway tutorial in the api-gateway repo — the numbered self-teaching YAML under distribution/tutorials/<category>/, its support files and README links, and the matching auto-discovered integration test. Use whenever the user asks to create, add, write, or scaffold a tutorial (or a tutorial step), even if they only describe the gateway behavior they want to teach and don't say the word "skill" or name the files.
# Create a Tutorial In this repo a tutorial is usually a **runnable, self-teaching YAML config**. The leading comment block of the file is the lesson; the config below it is the working example the reader runs. A complete tutorial therefore has three parts, and they must stay in sync: 1. **The numbered YAML** in `distribution/tutorials/<category>/` — the lesson plus the config. 2. **Category support files** — `membrane.sh`, `membrane.cmd`, a `README.md` (and `run-docker.*` where the category offers Docker), so the reader can actually start it. 3. **An integration test** under `distribution/src/test/java/com/predic8/membrane/tutorials/…` so the tutorial can't silently rot. These tests unzip the built distribution and start Membrane against the tutorial's YAML, so a broken example fails CI. A tutorial step can also be a **plain `.md` file** — use this when the step needs no local Membrane config (e.g. a curl-only walkthrough against a hosted API). A `.md` step gets no integration test; it just lives alongside the numbered YAMLs and is linked from the README. Work through the steps below in order. The reader of a tutorial follows the file top-to-bottom with no other docs open, so the single most important thing is that the YAML teaches itself. ## Step 1 — Choose the category and filename Categories live under `distribution/tutorials/`: `getting-started`, `json`, `xml`, `advanced`, `transformation`, `security`, `jwt`, `api-keys`, `oauth2`, `soap`, `soap-rest-converter`, `wsdl-to-openapi`, `orchestration`, `sse`, `mcp`, `llm-gateway`. Pick the one that fits; only create a new category if nothing fits (see "New category" at the end). Files are numbered `NN-Title.yaml` (e.g. `10-PathParameters.yaml`). The number sets reading order. List the directory and pick the next gap — convention leaves room between numbers (10, 20, 30…) so steps can be inserted later. Match the existing `PascalCase`-with-hyphens title style. ## Step 2 — Write the YAML (the lesson lives here) Open a sibling file in the same category and mirror its shape. Every tutorial YAML has the same skeleton: ```yaml # yaml-language-server: $schema=https://www.membrane-api.io/v7.2.4.json # # Tutorial: <Human Readable Name> # # <One sentence on what this teaches.> # # 1.) Start Membrane: # ./membrane.sh -c NN-Title.yaml # # 2.) Call the API: # curl localhost:2000/... # # <Expected output — paste it verbatim.> # # 3.) Continue with file NN-Next.yaml # the next step in the chain, if any api: port: 2000 ... ``` Hold to these conventions — they're what makes the set feel like one coherent course: - **Pin the schema line** to whatever sibling files use (grep one: `grep -m1 schema= distribution/tutorials/<category>/*.yaml`). It tracks the current release version, so don't invent a number. - **Keep the comment block short.** Steps are a command and its expected output — nothing more. Don't explain what the config does; the reader can see the config. Don't repeat information the `name:` field already provides. - **No section-header comments** above each `api:` block. The `name:` field describes the API. - **Inline comments only for the non-obvious WHY** — a surprising built-in, a hidden constraint. Never comment what the interceptor name already says. - **Keep the config minimal and focused** on the one concept the step teaches. Tutorials favor clarity over completeness; richer setups belong under `distribution/examples/`. - **Port 2000** is the house convention for tutorial listeners unless the lesson needs more. - Use `return: status: 200` not `return: {}`. - If this file continues a chain, end the previous file's comment block with a "Continue with file NN-Title.yaml" pointer to the new one. ## Step 3 — Make the category runnable The reader runs `./membrane.sh -c <file>` from the category directory, so that directory needs the launcher scripts and a README. - If `membrane.sh` / `membrane.cmd` are missing from the category dir, copy them verbatim from a sibling category — they are identical generic launchers that walk up to find `MEMBRANE_HOME`. - Copy `run-docker.sh` / `run-docker.cmd` only if the category documents a Docker workflow. - **`README.md`**: the category README points at the first file in the chain ("open [NN-First.yaml](NN-First.yaml) and follow the instructions there"). If you added the first file of a new category, write this README modeled on a sibling's. ## Step 4 — Write the integration test (don't skip this) Without a test the example will drift out of sync with the code and break unnoticed — that's the whole reason these tests exist. The harness starts Membrane from the **built distribution** with your YAML and lets you assert against the live gateway. Test package mirrors the category but with **underscores**: dir `getting-started` → package `com.predic8.membrane.tutorials.getting_started`. The `ExampleTests` `@Suite` selects the whole `com.predic8.membrane.tutorials` package, so a test placed correctly is **auto-discovered — no registration needed.** Each category has an `Abstract<Category>TutorialTest` that fixes `getTutorialDir()` (this is where the hyphen dir name is restored). Your concrete test extends it, names the YAML, and asserts: ```java /* <copy the Apache 2.0 license header verbatim from a sibling test — keep its year> */ package com.predic8.membrane.tutorials.<category_with_underscores>; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.containsString; public class <Name>TutorialTest extends Abstract<Category>TutorialTest { @Override protected String getTutorialYaml() { return "NN-Title.yaml"; } @Test void <whatItVerifies>() { // @formatter:off given() .when() .get("http://localhost:2000/...") .then() .statusCode(200) .body(containsString("...")); // @formatter:on } } ``` Notes: - Copy the license header from an existing sibling test rather than retyping it, so the year and wording match the repo. - Assert the **observable behavior the YAML comments promise** in Step 2 — if the lesson says "you should see Customer: 7", assert exactly that. The test is the executable form of the lesson. - If you created a brand-new category, also create its `Abstract<Category>TutorialTest` modeled on an existing one — it just returns the hyphenated dir name from `getTutorialDir()`. - **Sub-categories** (e.g. `llm-gateway/google`) need their own abstract base returning the full relative path (`llm-gateway/google`), not just the top-level category abstract. Model on `AbstractAiTutorialTest` / `AbstractGoogleTutorialTest` in `distribution/src/test/java/com/predic8/membrane/tutorials/`. - **`.md` steps** have no integration test — skip this step for them. ## Step 5 — Verify Run the test through the existing `run-example-test` skill. Pass `-b` so the distribution is rebuilt first: the tutorial YAML ships **inside** `distribution/target/*.zip`, and the tests run from the unzipped distribution, so an un-rebuilt run would test the old YAML (or none). ```bash .claude/skills/run-example-test/run-example-test.sh -b <Name>TutorialTest ``` A green run means the example actually starts and behaves as the lesson claims. ## Common pitfalls ### Template JSON auto-quoting When a `template` interceptor has `contentType: application/json`, the engine **automatically adds quotes** around every `${...}` interpolation. Writing `"${expr}"` produces `""value""` and breaks JSON. Always write interpolations **unquoted**: ```yaml - template: contentType: application/json src: | { "sub": ${user()}, "aud": "demo-resource" } ``` String literals that are not interpolated keep their quotes as normal. ### Built-in functions in Groovy templates Prefer the built-ins over raw property access: - `user()` — authenticated username (from `basicAuthentication` security scheme) - `scopes()` — list of granted scopes - `hasScope("read")` — boolean scope check Avoid the verbose `${property.'membrane.security.schemes'[0].username}` — use `${user()}` instead. ## New category (only when nothing fits) 1. `distribution/tutorials/<new-category>/` with the first `NN-*.yaml`, `membrane.sh`, `membrane.cmd`, and a `README.md`. 2. Add a section linking the new category in `distribution/tutorials/README.md`. 3. Create `com.predic8.membrane.tutorials.<new_category>.Abstract<NewCategory>TutorialTest` extending `AbstractMembraneTutorialTest`, returning the hyphenated dir from `getTutorialDir()`. 4. Add concrete tests as in Step 4. ## Checklist - [ ] `NN-Title.yaml` (or `.md` for curl-only steps) created with schema line, short numbered comment block, minimal config - [ ] Comment block: steps = command + expected output only; no prose repeating the config - [ ] No section-header comments above `api:` blocks; `name:` describes them - [ ] `return: status: 200` used (not `return: {}`) - [ ] Template interpolations unquoted when `contentType: application/json` - [ ] Previous file in the chain points to it ("Continue with…"), if applicable - [ ] Category has `membrane.sh`, `membrane.cmd`, `README.md` (and `run-docker.*` if used) - [ ] `<Name>TutorialTest` in the underscore package, extends the category abstract base, has the license header - [ ] Sub-category has its own abstract base returning the full relative path - [ ] `.md` steps have no integration test - [ ] Assertions match the behavior the YAML comments promise - [ ] `run-example-test.sh -b <Name>TutorialTest` is green
在 GitHub 查看