6 toolsOAuth bearer
gforms_example.py
Authenticating
This pack takes a Google OAuth bearer token, andconfigure() is optional when $GOOGLE_ACCESS_TOKEN is set. Which credential provider you hand it depends on whose account the calls run as.
Refer to Google’s provider page for the “GOOGLE” constant the snippets below name, the scopes these 6 tools ask for, and this server’s refresh behaviour.
A token you hold
For a script, or a notebook.EnvTokenProvider re-reads the variable on every call, so a token rotated beside the process is picked up without a restart; StaticTokenProvider takes one you already hold as a string. Neither renews anything, so the calls stop when the token expires.
gforms_script.py
One account, refreshed
For an agent or a server acting as you.OAuth2Client turns a client registration and a stored refresh token into an access token, and renews it before it lapses.
gforms_agent.py
No refresh token yet? Your own account is the one-time consent flow that hands you one.
Many end users
For a product whose users each connect their own account.SubjectProvider builds one credential per user through a factory you write, and use_subject names the user a call acts for. Your users’ accounts is the consent route inside your app; serving many users is the per-subject cache and its eviction.
gforms_server.py
forms.responses.readonly, which forms.body does not
cover, so forms_responses_get and forms_responses_list carry it instead of
the pack default. A consent screen built with
scopes_for over gforms.TOOLS asks for both.
The client
oauth_tool_factory is the whole client: a thin wrapper over httpx that attaches your token and these endpoint constants to each request. google-api-python-client and google-auth do not enter your dependency tree.credentials is whichever of the three you built in Authenticating. A pack takes it through configure(); a client you build takes the same object as credential_provider, and has no configure() of its own.
Paging through a list
A cursor belongs to the tool that returns it, so it is declared on that tool’s builder call:gforms_pagination.py
Pagination is declared on
forms_responses_list. The other 5 take no cursor.Tools
Each is aTool, called with
ainvoke as in the snippet above. The name
links to its parameters, its response and what it costs.
Formsforms_createPOSTCreate a new form from a title.forms_getGETRead a form: its title and description, its settings, and every item in order with the item and question IDs.forms_batch_updatePOSTChange a form with a batch of updates.forms_set_publish_settingsPOSTPublish or unpublish a form, and turn response collection on or off.Form responsesforms_responses_getGETRead one submitted response by ID.forms_responses_listGETList a form’s submitted responses, newest page first, up to 5000 per page.
Creating a form takes two calls
forms.create copies info.title and info.documentTitle and nothing else.
Google’s own reference calls the description, the items and the settings
disallowed there. Questions arrive in a second call:
gforms_two_calls.py
Form, so the schema cannot offer a field the endpoint rejects.
One Info, two contracts
documentTitle can be set on create and cannot be modified by a batchUpdate.
The form description is the other way round. That is one resource with two field
sets, so Info carries Mode markers and each tool
declares which operation it is:
Writing two near-identical models instead would put every field description in
two places, and nothing keeps those in step.
Gotchas
Indices shift as the batch applies
Indices shift as the batch applies
Items are addressed by index, and a
create_item at index 2 moves every
later item down before the next request in the same batch reads its own
location. Order several insertions back-to-front, or state each location
against the form as it will be by then. Nothing in the API warns you.forms_get returns Google's payload untouched
forms_get returns Google's payload untouched
Google’s documented way to change a question is to read the form, edit your
copy of the item and write it back through
update_item with the IDs
unchanged. An item that came back reshaped is an item that cannot be written
back, so this tool has no response handler. The responses collection is
trimmed, because nothing is written back there.Publishing takes both flags
Publishing takes both flags
is_published and is_accepting_responses are both required when you set
the publish state. Accepting responses while unpublished is refused by the
API, and the schema refuses it locally, so that combination never costs a
round trip. Legacy forms have no publishSettings field and this endpoint
does not support them.Answers come back keyed by question ID
Answers come back keyed by question ID
A response holds
answers keyed by questionId, and nothing in it names
the question. forms_get is where the ID maps back to the wording, so read
the form once and keep the mapping rather than fetching it per response.File upload questions cannot be created
File upload questions cannot be created
Google does not support creating one through the API.
FileUploadQuestion
is still modelled, because an existing one comes back on forms_get and can
be moved or deleted, and because its answers name Drive file IDs a caller
can act on.Deleting a form is a Drive operation
Deleting a form is a Drive operation
So is changing who can open it. The Forms API creates and edits; the file
itself belongs to Drive. Use
gdrive for both.Related
- Google: consent screen, scopes, refresh
- The mode system: one resource, two operations, different field sets
- Google Drive: deleting a form, and sharing it