canvas-lms/app/graphql
Chris Soto 5bee58bc2e add rubric save as draft button
this commit adds a save as draft button to the rubric form. This button
is only displayed when a rubric has no current associations. When
clicked, the rubric is saved with a new workflow_state value of "draft".

closes EVAL-3637
flag=enhanced_rubrics

test plan:
- make sure graphql is updated
- create a rubric. you should see the "Save as Draft" button
- add a title and save as draft. you should be navigated back to the
  rubric list and see the rubric with a draft status
- edit the rubric and the click the "Save" button. you should be
  navigated back to the rubric list and the rubric should no longer have
  the draft status.
- edit the rubric. you should see the "Save as Draft" button still
  available.
- add that same rubric to an assignment.
- navigate back to the edit rubric page. you should no longer see the
  "Save as Draft" button.

Change-Id: Id1e68f1432cd17fab618b2c8dd0350acf0408e12
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/341595
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Derek Williams <derek.williams@instructure.com>
Reviewed-by: Kai Bjorkman <kbjorkman@instructure.com>
QA-Review: Kai Bjorkman <kbjorkman@instructure.com>
Product-Review: Ravi Koll <ravi.koll@instructure.com>
2024-03-01 21:42:58 +00:00
..
graphql_helpers Rubocop for ruby 3.1 2023-06-06 16:44:26 +00:00
interfaces add previewUrl to graphql submission interface 2024-02-29 20:02:39 +00:00
loaders Improve ignored_columns handling 2024-03-01 01:10:34 +00:00
mutations match assignment with legacy for graded discussion 2024-02-07 17:40:17 +00:00
tracers remove unused tags from `graphql.operation.*` metrics 2024-02-23 00:16:53 +00:00
types add rubric save as draft button 2024-03-01 21:42:58 +00:00
README.md Read credentials from rails credentials for access token 2021-10-22 14:50:48 +00:00
audit_log_field_extension.rb bundle update rubocop 2023-12-06 14:25:02 +00:00
canvas_schema.rb Create usage rights type 2023-11-08 17:53:09 +00:00
collection_connection.rb Create new custom graphql connection for collection structures 2022-12-20 20:42:40 +00:00
dynamo_connection.rb RuboCop: Style grab bag 2021-11-20 03:04:04 +00:00
dynamo_query.rb Rubocop for ruby 3.1 2023-06-06 16:44:26 +00:00
graphql_helpers.rb RuboCop: Style/BlockDelimiters, Style/Lambda 2021-11-23 21:30:47 +00:00
graphql_node_loader.rb Create usage rights type 2023-11-08 17:53:09 +00:00
graphql_postgres_timeout.rb Remove more settings 2024-01-29 16:21:38 +00:00
patched_array_connection.rb RuboCop: Style/ClassCheck, Style/ClassEqualityComparison 2021-11-19 22:39:31 +00:00
types.rb add # frozen_string_literal: true for graphql 2020-10-27 15:56:39 +00:00

README.md

GraphQL in Canvas

Canvas has a "first-class" GraphQL data graph that is publicly exposed on an API endpoint. This is already well-documented.

Apollo Federation

In addition to the standard GraphQL endpoint, Canvas exposes a "subgraph" endpoint whose schema is suitable for use in an Apollo Federation. This is the same schema, but extended according to the Apollo Federation specification, and with some Federation directives applied to various fields and types.

The apollo-federation gem is used to add Federation directives to this subgraph. While it is important that the public-facing graph does not include Federation extensions, the gem's features can be used freely on any type or field. They are simply ignored in the public-facing graph, and do not show up in its schema.

Promoting an Object Type to a Federation Entity

A Federation entity is an object type whose definition spans multiple subgraphs. One subgraph provides its canonical definition, and others extend it.

To promote an existing type to an entity with its canonical definition in Canvas, declare one or more key fields and implement ::resolve_reference. E.g.:

module Types
  class CourseType < ApplicationObjectType
    key fields: "id"
    def self.resolve_reference(reference, context)
      legacy_id = GraphQLHelpers.parse_relay_id(reference[:id], "Course")
      GraphQLNodeLoader.load("Course", legacy_id, context)
    end
  end
end

We expect to promote many types in exactly this way, so we've built a helper for it. If a type's id field is a Relay-style "global" id (as most are), and you want to promote that type with only a single @key directive on the id field, i.e. @key(fields: "id"), you can just use key_field_id like so:

module Types
  class CourseType < ApplicationObjectType
    key_field_id
  end
end

See the gem usage docs for more examples and guidance on using Federation features, including how to extend entities whose canonical definition resides in an external subgraph.

Smoke Testing the Federation Subgraph

In deployed environments, only an Apollo API Gateway will be able to query the federation subgraph. However if you need to smoke test it locally, this is the way.

  1. Generate two RSA keypairs and designate one your signing key, the other your encryption key.

  2. Copy config/vault_contents.yml.example to config/vault_contents.yml, then replace the development.'app-canvas/data/secrets'.data.inst_access_signature.private_key with the base64-encoded representation of the private key of your signing keypair, and the development.'app-canvas/data/secrets'.data.inst_access_signature.encryption_public_key with the base64-encoded representation of the public key of your encryption keypair.

  3. Start up your Canvas server and get yourself an API access token, e.g. by following the "Manual Token Generation" section of the OAuth docs.

  4. Export that thing as API_TOKEN and use it to get yourself an unencrypted InstAccess token, e.g.:

   $ curl 'http://localhost:3000/api/v1/inst_access_tokens?unencrypted=1' \
     -X POST \
     -H 'Authorization: Bearer $API_TOKEN'
  1. Now export that thing as INST_ACCESS and use it to issue a query to the subgraph, e.g.:
   $ curl http://localhost:3000/api/graphql/subgraph \
   -X POST \
   -H "Accept: application/json" \
   -H "Content-type: application/json" \
   -H "Authorization: Bearer $INST_ACCESS" \
   --data '
   {
     "query": "query ($_representations: [_Any!]!) { _entities(representations: $_representations) { ... on Course { name } } }",
     "variables": {
       "_representations": [
         {
           "__typename": "Course",
           "id": "Q291cnNlLTE="
         }
       ]
     }
   }'

The above query should return a result that includes the name of Course 1, as long as it exists and the user you used to get the initial access token has permission to read it.