aws-amplify / amplify-category-api

The AWS Amplify CLI is a toolchain for simplifying serverless web and mobile development. This plugin provides functionality for the API category, allowing for the creation and management of GraphQL and REST based backends for your amplify project.
https://docs.amplify.aws/
Apache License 2.0
89 stars 77 forks source link

Specify an Initial value for create (like .default() but never overrided by user values) #2724

Open naedx opened 3 months ago

naedx commented 3 months ago

Describe the feature you'd like to request

Given the following schema:


const schema = a.schema({
  Todo: a.model({
    updatesCount: a.number().default(0),
    content: a.string(),
    completed: a.boolean().default(false),
  })
  .authorization(allow => [allow.publicApiKey()]),
});

I would like to be able to specify an initial value for specific fields. I want Todo.updatesCount to always have the initial value of 0 and Todo.completed to have an initial value of false.

Describe the solution you'd like

I would like to be able to set the initial value with .initial(val: any).


const schema = a.schema({
  Todo: a.model({
    updatesCount: a.number().initial(0),
    content: a.string(),
    completed: a.boolean().initial(false).default(false),
  })
  .authorization(allow => [allow.publicApiKey()]),
});

Describe alternatives you've considered

An alternative is to set the default value and then define a restrictive input createTodoInput that does not contain the value. Since the user cannot submit the value the default value is always used. This is subject to the user's permission to set the value.


input CreateTodoInput {
    ## updatesCount: number,
    content: a.string(),
    ## completed: bool,
}

Additional context

The values for .default() are set in the init pipeline resolver:

## Resolver: MutationcreateTodoinit0Function

## [Start] Initialization default values. **
$util.qr($ctx.stash.put("defaultValues", $util.defaultIfNull($ctx.stash.defaultValues, {})))
$util.qr($ctx.stash.defaultValues.put("id", $util.autoId()))
#set( $createdAt = $util.time.nowISO8601() )
$util.qr($ctx.stash.defaultValues.put("createdAt", $createdAt))
$util.qr($ctx.stash.defaultValues.put("updatedAt", $createdAt))
$util.toJson({
  "version": "2018-05-29",
  "payload": {}
})
## [End] Initialization default values. **

...which are then superceded by user values when merged in:

## Resolver: MutationCreateTodoDataResolverFn

## Set the default values to put request **
#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) )
## copy the values from input **
$util.qr($mergedValues.putAll($util.defaultIfNull($args.input, {})))

# ...

A simple implementation for .initial() could store values as $ctx.stash.initialValues and merge them in on top of user values. However, this approach would be subject to the user's authorization allowing them to set these values.

A more complete implementation would set these values separately from the user submitted values. This would allow initial values to be set even if the user does not have write permissions on those fields.

Is this something that you'd be interested in working on?

Would this feature include a breaking change?

dpilch commented 3 months ago

You can workaround this limitation by creating a custom mutation that has the desired input or overrides the values for updatesCount and completed. Then scope the auth rules to disallow create through the auto-generated create mutation.

https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/