diff --git a/content/get-started/learning-to-code/index.md b/content/get-started/learning-to-code/index.md
index 75d48adb5ef6..cef59dc7080f 100644
--- a/content/get-started/learning-to-code/index.md
+++ b/content/get-started/learning-to-code/index.md
@@ -7,7 +7,9 @@ children:
- /getting-started-with-git
- /finding-and-understanding-example-code
- /reusing-other-peoples-code-in-your-projects
+ - /setting-up-copilot-for-learning-to-code
- /learning-to-debug-with-github-copilot
- /storing-your-secrets-safely
shortTitle: Learn to code
---
+
diff --git a/content/get-started/learning-to-code/setting-up-copilot-for-learning-to-code.md b/content/get-started/learning-to-code/setting-up-copilot-for-learning-to-code.md
new file mode 100644
index 000000000000..ff75f2a2e442
--- /dev/null
+++ b/content/get-started/learning-to-code/setting-up-copilot-for-learning-to-code.md
@@ -0,0 +1,67 @@
+---
+title: Setting up Copilot for learning to code
+intro: 'Configure {% data variables.product.prodname_copilot_short %} to help you learn coding concepts and actively build your programming skills.'
+versions:
+ fpt: '*'
+topics:
+ - Copilot
+shortTitle: Set up Copilot for learning
+---
+
+## Can {% data variables.product.prodname_copilot_short %} help me learn to code?
+
+Yes! {% data variables.product.prodname_copilot_short %} can adapt to meet your changing needs throughout your coding journey. When you're an experienced developer, you'll use {% data variables.product.prodname_copilot_short %} as a coding assistant. While you're learning to code, it's more beneficial as a **supportive companion**.
+
+In this guide, you’ll learn how to set up {% data variables.product.prodname_copilot_short %} to act as a **tutor** that will help you build a deep understanding of programming concepts, rather than relying on it to write your code for you. To optimize your learning, follow these steps for each repository you work on!
+
+## Prerequisites
+
+This guide assumes that you'll use {% data variables.product.prodname_copilot_short %} in {% data variables.product.prodname_vscode_shortname %}. To get set up, see [Set up Copilot in {% data variables.product.prodname_vscode_shortname %}](https://code.visualstudio.com/docs/copilot/setup-simplified) in the {% data variables.product.prodname_vscode %} documentation.
+
+## Step 1: Disable code completions
+
+First, let's disable code completions. This will give you the opportunity to deepen your understanding of programming concepts by writing more code yourself.
+
+1. In {% data variables.product.prodname_vscode_shortname %}, open your project.
+1. Create a folder in the root directory called `.vscode`.
+1. Inside `.vscode`, create a file called `settings.json`.
+1. Add the following text to the file:
+
+ ```json copy
+ {
+ "github.copilot.enable": {
+ "*": false
+ }
+ }
+ ```
+
+1. Save the file. {% data variables.product.prodname_copilot_short %} code completions are now disabled for this project in {% data variables.product.prodname_vscode_shortname %}.
+
+## Step 2: Add learning instructions
+
+Now, let's provide {% data variables.product.prodname_copilot_chat_short %} with instructions to act like a tutor that supports your learning.
+
+1. In the root folder of your project, create a file called `copilot-instructions.md`.
+1. Add the following text, or customize it for your personal learning goals:
+
+ ```markdown copy
+ I am learning to code. You are to act as a tutor; assume I am a beginning coder. Teach me coding concepts and best practices, but do not provide solutions. Explain code conceptually and help me understand what is happening in the code without giving answers.
+
+ Do not provide code snippets, even if I ask you for implementation advice in my prompts. Teach me all the basic coding concepts in your answers. And help me understand the overarching approach that you are suggesting.
+
+ Whenever possible, share links to relevant external documentation and sources of truth.
+
+ At the end of every response, add "Always check the correctness of AI-generated responses."
+ ```
+
+1. Save the file. {% data variables.product.prodname_copilot_short %} will use these instructions when you ask questions in {% data variables.product.prodname_copilot_chat_short %}.
+
+## Step 3: Use {% data variables.product.prodname_copilot_chat_short %} to learn
+
+You're ready to start building real coding skills with {% data variables.product.prodname_copilot_short %}'s help!
+
+Throughout your work on the project, engage in a long-running conversation with **{% data variables.product.prodname_copilot_chat_short %}**. Treat it as your **personal tutor**, asking questions as they arise and using it to navigate challenges or clarify concepts.
+
+> [!TIP] You can open {% data variables.product.prodname_copilot_chat_short %} with a keyboard shortcut: Ctrl+Alt+I (Windows/Linux) or Command+Shift+I (Mac).
+
+{% data variables.product.prodname_copilot_chat_short %} is especially helpful for debugging your code. For step-by-step guidance, see [AUTOTITLE](/get-started/learning-to-code/learning-to-debug-with-github-copilot).
diff --git a/src/events/middleware.ts b/src/events/middleware.ts
index 2b1210c1b268..69d52a4f9db1 100644
--- a/src/events/middleware.ts
+++ b/src/events/middleware.ts
@@ -47,10 +47,19 @@ router.post(
const validEvents: any[] = []
const validationErrors: any[] = []
+ // We use a LRU cache & a hash of the request IP + error message
+ // to prevent sending multiple validation errors per user that can spam requests to Hydro
+ const getValidationErrorHash = (validateErrors: ErrorObject[]) =>
+ `${req.ip}:${(validateErrors || [])
+ .map(
+ (error: ErrorObject) => error.message + error.instancePath + JSON.stringify(error.params),
+ )
+ .join(':')}`
+
for (const eventBody of eventsToProcess) {
try {
+ // Skip event if it doesn't have a type or if the type is not in the allowed types
if (!eventBody.type || !allowedTypes.has(eventBody.type)) {
- validationErrors.push({ event: eventBody, error: 'Invalid type' })
continue
}
const type: EventType = eventBody.type
@@ -71,18 +80,7 @@ router.post(
}
const validate = validators[type]
if (!validate(body)) {
- validationErrors.push({
- event: body,
- error: validate.errors || [],
- })
- // This protects so we don't bother sending the same validation
- // error, per user, more than once (per time interval).
- // This helps if we're bombarded with junk bot traffic. So it
- // protects our Hydro instance from being overloaded with things
- // that aren't helping anybody.
- const hash = `${req.ip}:${(validate.errors || [])
- .map((error: ErrorObject) => error.message + error.instancePath)
- .join(':')}`
+ const hash = getValidationErrorHash(validate.errors || [])
if (!sentValidationErrors.has(hash)) {
sentValidationErrors.set(hash, true)
formatErrors(validate.errors || [], body).map((error) => {
diff --git a/src/events/tests/middleware.ts b/src/events/tests/middleware.ts
index 37a75ef220ae..6da5601bdfd4 100644
--- a/src/events/tests/middleware.ts
+++ b/src/events/tests/middleware.ts
@@ -6,7 +6,6 @@ describe('POST /events', () => {
vi.setConfig({ testTimeout: 60 * 1000 })
async function checkEvent(data: any) {
- // if data is not an array, make it one
if (!Array.isArray(data)) {
data = [data]
}
@@ -89,9 +88,9 @@ describe('POST /events', () => {
})
test('should require a type', async () => {
- const { statusCode, body } = await checkEvent({ ...pageExample, type: undefined })
- expect(statusCode).toBe(400)
- expect(body).toContain('"error":"Invalid type"}')
+ const { statusCode } = await checkEvent({ ...pageExample, type: undefined })
+ // should skip events with no type
+ expect(statusCode).toBe(200)
})
test('should require an event_id in uuid', async () => {