Skip to content

Commit 80ab47e

Browse files
authored
Merge branch 'main' into issues/1174_check-whether-user-can-use-profile-api
2 parents 6ca2e34 + 40690e1 commit 80ab47e

4 files changed

Lines changed: 59 additions & 50 deletions

File tree

.github/copilot-instructions.md

Lines changed: 0 additions & 42 deletions
This file was deleted.

AGENTS.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Project Overview
2+
- Rails 7.1 monolith for the Raspberry Pi Code Editor API (REST + GraphQL), served at `editor-api.raspberrypi.org`.
3+
- Primary runtime via Docker; API listens on port 3009.
4+
5+
## Architecture
6+
- **REST** under `app/controllers/api/**` with Jbuilder views in `app/views/api/**`; **GraphQL** at `/graphql` (schema in `app/graphql/**`).
7+
- **Auth**: Browser/session via OmniAuth (OIDC to Hydra); API token via `Authorization: Bearer` with `Identifiable#identify_user``User.from_token``HydraPublicApiClient`.
8+
- **Authorization**: cancancan in `app/models/ability.rb`. Use `load_and_authorize_resource` in controllers; GraphQL uses `Types::ProjectType.authorized?` and `current_ability` in context.
9+
- **Domain**: `Project` (+ `Component`) with Active Storage attachments. Domain operations in `lib/concepts/**` (e.g. `Project::Create`, `Project::CreateRemix`). Prefer calling these from controllers/mutations.
10+
- **Jobs**: GoodJob (`bundle exec good_job start --max-threads=8`). Admin UI at `/admin/good_job`.
11+
- **Integrations**: Profile API (`lib/profile_api_client.rb`), UserInfo API, GitHub GraphQL (`lib/github_api.rb`), GitHub webhooks via `GithubWebhooksController`.
12+
- **Storage/CORS**: Active Storage uses S3 in non-dev. CORS via `config/initializers/cors.rb` and `lib/origin_parser.rb`. `CorpMiddleware` sets CORP for Active Storage routes.
13+
14+
## Key Conventions
15+
- GraphQL context: `current_user`, `current_ability`, `remix_origin`. Object IDs use GlobalID. Locale fallback via `ProjectLoader`: `[requested, 'en', nil]`.
16+
- REST pagination returns HTTP `Link` header (see `Api::ProjectsController#pagination_link_header`).
17+
- Project rules: identifiers unique per locale; default component name/extension immutable on update; students cannot update `instructions` on school projects; creating a project in a school auto-builds `SchoolProject`.
18+
- Remix: `Project::CreateRemix` clones media/components, sets `remix_origin`, clears `lesson_id`.
19+
- Errors: domain ops return `OperationResponse` with `:error`; controllers return 4xx heads; GraphQL raises `GraphQL::ExecutionError`. Exceptions reported to Sentry.
20+
- snake_case for variable numbers (exceptions: `sha256`, `X-Hub-Signature-256`).
21+
22+
## Quickstart
23+
```bash
24+
cp .env.example .env
25+
docker compose build
26+
docker compose run --rm api rails db:setup
27+
docker compose up
28+
```
29+
30+
## Development
31+
- Use `docker compose` for all commands; project mounts into `editor-api:builder` with tmpfs for `tmp/`.
32+
33+
## Testing
34+
- Full suite: `docker compose run --rm api rspec`
35+
- Single spec: `docker compose run --rm api rspec spec/path/to/spec.rb`
36+
- Lint: `docker compose run --rm api bundle exec rubocop`
37+
- CI: CircleCI with Ruby 3.2, Postgres 12, Redis.
38+
39+
## Where to Look First
40+
- Routes: `config/routes.rb`. Auth: `config/initializers/omniauth.rb`, `app/helpers/authentication_helper.rb`, `app/controllers/concerns/identifiable.rb`.
41+
- Permissions: `app/models/ability.rb`. Domain ops: `lib/concepts/**`. Models: `app/models/**`. GraphQL: `app/graphql/**`.
42+
43+
## Security
44+
- Never commit secrets (`.env`, `config/master.key`, API tokens, webhook secrets).
45+
- `.env.example` contains placeholder values only.

lib/tasks/seeds_helper.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ module SeedsHelper
66
john_doe: 'bbb9b8fd-f357-4238-983d-6f87b99bdbb2', # john.doe@example.com
77
jane_smith: 'e52de409-9210-4e94-b08c-dd11439e07d9', # student
88
john_smith: '0d488bec-b10d-46d3-b6f3-4cddf5d90c71', # student
9-
emily_ssouser: '88e0aed6-8f20-4e40-98f9-610a0ab1cfcc' # sso student
9+
emily_ssouser: '88e0aed6-8f20-4e40-98f9-610a0ab1cfcc', # sso student
10+
jim_dun: '019a649f-87f3-483b-8eea-39a05c324264' # user with no school
1011
}.freeze
1112

1213
# Match the school in profile...

lib/tasks/test_seeds.rake

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,31 @@ namespace :test_seeds do
1212
Rails.logger.info 'Destroying existing seeds...'
1313
creator_id = ENV.fetch('SEEDING_CREATOR_ID', TEST_USERS[:jane_doe])
1414
teacher_id = ENV.fetch('SEEDING_TEACHER_ID', TEST_USERS[:john_doe])
15+
teacher_signup_id = TEST_USERS[:jim_dun]
1516

1617
# Hard coded as the student's school needs to match
1718
student_ids = [TEST_USERS[:jane_smith], TEST_USERS[:john_smith], TEST_USERS[:emily_ssouser]]
1819
school_id = TEST_SCHOOL
20+
teacher_signup_school_id =
21+
School.find_by(creator_id: teacher_signup_id)&.id
1922

2023
# Remove the roles first
24+
Role.where(user_id: teacher_signup_id).destroy_all
2125
Role.where(user_id: [creator_id, teacher_id] + student_ids).destroy_all
2226

2327
# Destroy the project and then the lesson itself (The lesson's `before_destroy` prevents us using destroy)
2428
lesson_ids = Lesson.where(school_id:).pluck(:id)
25-
Project.where(lesson_id: [lesson_ids]).destroy_all
26-
Lesson.where(id: [lesson_ids]).delete_all
29+
Project.where(lesson_id: lesson_ids).destroy_all
30+
Lesson.where(id: lesson_ids).delete_all
2731

2832
# Destroy the class members and then the class itself
29-
school_class_ids = SchoolClass.where(school_id:).pluck(:id)
30-
ClassStudent.where(school_class_id: [school_class_ids]).destroy_all
31-
SchoolClass.where(id: [school_class_ids]).destroy_all
33+
school_class_ids = SchoolClass.where(school_id: [school_id, teacher_signup_school_id].compact).pluck(:id)
34+
ClassStudent.where(school_class_id: school_class_ids).destroy_all
35+
SchoolClass.where(id: school_class_ids).destroy_all
3236

33-
# Destroy the school
34-
School.find(school_id).destroy
37+
# Destroy the schools
38+
school_ids = [school_id, teacher_signup_school_id].compact
39+
School.where(id: school_ids).destroy_all
3540

3641
Rails.logger.info 'Done...'
3742
rescue StandardError => e

0 commit comments

Comments
 (0)