diff --git a/app/config/errors.php b/app/config/errors.php index a6d97ddacc..42e58cf4f6 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -112,12 +112,12 @@ return [ ], Exception::USER_ALREADY_EXISTS => [ 'name' => Exception::USER_ALREADY_EXISTS, - 'description' => 'A user with the same id, email, or phone already exists in your project.', + 'description' => 'A user with the same id, email, or phone already exists in this project.', 'code' => 409, ], Exception::USER_BLOCKED => [ 'name' => Exception::USER_BLOCKED, - 'description' => 'The current user has been blocked.', + 'description' => 'The current user has been blocked. You can unblock the user by making a request to the User API\'s "Update User Status" endpoint or in the Appwrite Console\'s Auth section.', 'code' => 401, ], Exception::USER_INVALID_TOKEN => [ @@ -177,12 +177,12 @@ return [ ], Exception::USER_PASSWORD_RECENTLY_USED => [ 'name' => Exception::USER_PASSWORD_RECENTLY_USED, - 'description' => 'The password you are trying to use is similar to your previous password. Please choose a stronger password.', + 'description' => 'The password you are trying to use is similar to your previous password. For your security, please choose a different password and try again.', 'code' => 400, ], Exception::USER_PASSWORD_PERSONAL_DATA => [ 'name' => Exception::USER_PASSWORD_PERSONAL_DATA, - 'description' => 'The password you are trying to use contains references to your name, email, phone or userID. Please choose a different password and try again.', + 'description' => 'The password you are trying to use contains references to your name, email, phone or userID. For your security, please choose a different password and try again.', 'code' => 400, ], Exception::USER_SESSION_NOT_FOUND => [ @@ -192,7 +192,7 @@ return [ ], Exception::USER_IDENTITY_NOT_FOUND => [ 'name' => Exception::USER_IDENTITY_NOT_FOUND, - 'description' => 'The identity could not be found.', + 'description' => 'The identity could not be found. Please sign in with OAuth provider to create identity first.', 'code' => 404, ], Exception::USER_UNAUTHORIZED => [ @@ -254,7 +254,7 @@ return [ ], Exception::TEAM_INVALID_SECRET => [ 'name' => Exception::TEAM_INVALID_SECRET, - 'description' => 'The team invitation secret is invalid.', + 'description' => 'The team invitation secret is invalid. Please request a new invitation and try again.', 'code' => 401, ], Exception::TEAM_MEMBERSHIP_MISMATCH => [ @@ -269,7 +269,7 @@ return [ ], Exception::TEAM_ALREADY_EXISTS => [ 'name' => Exception::TEAM_ALREADY_EXISTS, - 'description' => 'Team with requested ID already exists.', + 'description' => 'Team with requested ID already exists. Please choose a different ID and try again.', 'code' => 409, ], @@ -281,7 +281,7 @@ return [ ], Exception::MEMBERSHIP_ALREADY_CONFIRMED => [ 'name' => Exception::MEMBERSHIP_ALREADY_CONFIRMED, - 'description' => 'Membership already confirmed', + 'description' => 'Membership is already confirmed.', 'code' => 409, ], @@ -350,7 +350,7 @@ return [ ], Exception::STORAGE_BUCKET_ALREADY_EXISTS => [ 'name' => Exception::STORAGE_BUCKET_ALREADY_EXISTS, - 'description' => 'A storage bucket with the requested ID already exists.', + 'description' => 'A storage bucket with the requested ID already exists. Try again with a different ID or use "unique()" to generate a unique ID.', 'code' => 409, ], Exception::STORAGE_BUCKET_NOT_FOUND => [ @@ -370,24 +370,24 @@ return [ ], Exception::STORAGE_INVALID_APPWRITE_ID => [ 'name' => Exception::STORAGE_INVALID_APPWRITE_ID, - 'description' => 'The value for x-appwrite-id header is invalid. Please check the value of the x-appwrite-id header is valid id and not unique().', + 'description' => 'The value for x-appwrite-id header is invalid. Please check the value of the x-appwrite-id header is a valid id and not unique().', 'code' => 400, ], /** VCS */ Exception::INSTALLATION_NOT_FOUND => [ 'name' => Exception::INSTALLATION_NOT_FOUND, - 'description' => 'Installation with the requested ID could not be found.', + 'description' => 'Installation with the requested ID could not be found. Check to see if the ID is correct, or create the installation.', 'code' => 404, ], Exception::PROVIDER_REPOSITORY_NOT_FOUND => [ 'name' => Exception::PROVIDER_REPOSITORY_NOT_FOUND, - 'description' => 'VCS (Version Control System) repository with the requested ID could not be found.', + 'description' => 'VCS (Version Control System) repository with the requested ID could not be found. Check to see if the ID is correct, and if it belongs to installationId you provided.', 'code' => 404, ], Exception::REPOSITORY_NOT_FOUND => [ 'name' => Exception::REPOSITORY_NOT_FOUND, - 'description' => 'Repository with the requested ID could not be found.', + 'description' => 'Repository with the requested ID could not be found. Check to see if the ID is correct, or create the respository.', 'code' => 404, ], Exception::PROVIDER_CONTRIBUTION_CONFLICT => [ @@ -397,7 +397,7 @@ return [ ], Exception::GENERAL_PROVIDER_FAILURE => [ 'name' => Exception::GENERAL_PROVIDER_FAILURE, - 'description' => 'VCS (Version Control System) provider failed to proccess the request.', + 'description' => 'VCS (Version Control System) provider failed to proccess the request. We believe this is an error with the VCS provider. Try again, or contact support for more information.', 'code' => 400, ], @@ -414,7 +414,7 @@ return [ ], Exception::FUNCTION_ENTRYPOINT_MISSING => [ 'name' => Exception::FUNCTION_RUNTIME_UNSUPPORTED, - 'description' => 'Function entrypoint is not configured. Please specify it in function settings or when making deployment.', + 'description' => 'Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 'code' => 404, ], @@ -470,7 +470,7 @@ return [ ], Exception::COLLECTION_ALREADY_EXISTS => [ 'name' => Exception::COLLECTION_ALREADY_EXISTS, - 'description' => 'A collection with the requested ID already exists.', + 'description' => 'A collection with the requested ID already exists. Try again with a different ID or use "unique()" to generate a unique ID.', 'code' => 409, ], Exception::COLLECTION_LIMIT_EXCEEDED => [ @@ -492,17 +492,17 @@ return [ ], Exception::DOCUMENT_MISSING_DATA => [ 'name' => Exception::DOCUMENT_MISSING_DATA, - 'description' => 'The document data is missing. You must provide the document data.', + 'description' => 'The document data is missing. Try again with document data populated', 'code' => 400, ], Exception::DOCUMENT_MISSING_PAYLOAD => [ 'name' => Exception::DOCUMENT_MISSING_PAYLOAD, - 'description' => 'The document data and permissions are missing. You must provide either the document data or permissions to be updated.', + 'description' => 'The document data and permissions are missing. You must provide either document data or permissions to be updated.', 'code' => 400, ], Exception::DOCUMENT_ALREADY_EXISTS => [ 'name' => Exception::DOCUMENT_ALREADY_EXISTS, - 'description' => 'Document with the requested ID already exists.', + 'description' => 'Document with the requested ID already exists. Try again with a different ID or use "unique()" to generate a unique ID.', 'code' => 409, ], Exception::DOCUMENT_UPDATE_CONFLICT => [ @@ -544,7 +544,7 @@ return [ ], Exception::ATTRIBUTE_ALREADY_EXISTS => [ 'name' => Exception::ATTRIBUTE_ALREADY_EXISTS, - 'description' => 'Attribute with the requested ID already exists.', + 'description' => 'Attribute with the requested ID already exists. Try again with a different ID or use "unique()" to generate a unique ID.', 'code' => 409, ], Exception::ATTRIBUTE_LIMIT_EXCEEDED => [ @@ -576,7 +576,7 @@ return [ ], Exception::INDEX_ALREADY_EXISTS => [ 'name' => Exception::INDEX_ALREADY_EXISTS, - 'description' => 'Index with the requested ID already exists.', + 'description' => 'Index with the requested ID already exists. Try again with a different ID or use "unique()" to generate a unique ID.', 'code' => 409, ], Exception::INDEX_INVALID => [ @@ -593,7 +593,7 @@ return [ ], Exception::PROJECT_ALREADY_EXISTS => [ 'name' => Exception::PROJECT_ALREADY_EXISTS, - 'description' => 'Project with the requested ID already exists.', + 'description' => 'Project with the requested ID already exists. Try again with a different ID or use "unique()" to generate a unique ID.', 'code' => 409, ], Exception::PROJECT_UNKNOWN => [ @@ -633,37 +633,37 @@ return [ ], Exception::ROUTER_HOST_NOT_FOUND => [ 'name' => Exception::ROUTER_HOST_NOT_FOUND, - 'description' => 'Host is not trusted. Add a custom domain to your project first.', + 'description' => 'Host is not trusted. This could occur because you have not configured a custom domain. Add a custom domain to your project first and try again.', 'code' => 404, ], Exception::ROUTER_DOMAIN_NOT_CONFIGURED => [ 'name' => Exception::ROUTER_DOMAIN_NOT_CONFIGURED, - 'description' => 'Please configure domain environment variables before using Appwrite outside of localhost.', + 'description' => 'Domain environment variables not configured. Please configure domain environment variables before using Appwrite outside of localhost.', 'code' => 500, ], Exception::RULE_RESOURCE_NOT_FOUND => [ 'name' => Exception::RULE_RESOURCE_NOT_FOUND, - 'description' => 'Resource could not be found. Check resourceId and resourceType.', + 'description' => 'Resource could not be found. Please check if the resourceId and resourceType are correct, or if the resource actually exists.', 'code' => 404, ], Exception::RULE_NOT_FOUND => [ 'name' => Exception::RULE_NOT_FOUND, - 'description' => 'Rule with the requested ID could not be found.', + 'description' => 'Rule with the requested ID could not be found. Please check if the ID provided is correct or if the rule actually exists.', 'code' => 404, ], Exception::RULE_ALREADY_EXISTS => [ 'name' => Exception::RULE_ALREADY_EXISTS, - 'description' => 'Domain already used.', + 'description' => 'Domain is already used. Please try again with a different domain.', 'code' => 409, ], Exception::RULE_VERIFICATION_FAILED => [ 'name' => Exception::RULE_VERIFICATION_FAILED, - 'description' => 'Domain verification failed. Please check your DNS records.', + 'description' => 'Domain verification failed. Please check if your DNS records are correct and try again.', 'code' => 401, ], Exception::PROJECT_SMTP_CONFIG_INVALID => [ 'name' => Exception::PROJECT_SMTP_CONFIG_INVALID, - 'description' => 'Provided SMTP config is invalid.', + 'description' => 'Provided SMTP config is invalid. Please check the configured values and try again.', 'code' => 400, ], Exception::PROJECT_TEMPLATE_DEFAULT_DELETION => [ @@ -693,7 +693,7 @@ return [ ], Exception::VARIABLE_ALREADY_EXISTS => [ 'name' => Exception::VARIABLE_ALREADY_EXISTS, - 'description' => 'Variable with the same ID already exists in your project.', + 'description' => 'Variable with the same ID already exists in this project. Try again with a different ID.', 'code' => 409, ], Exception::GRAPHQL_NO_QUERY => [ @@ -710,17 +710,17 @@ return [ /** Migrations */ Exception::MIGRATION_NOT_FOUND => [ 'name' => Exception::MIGRATION_NOT_FOUND, - 'description' => 'Migration with the requested ID could not be found.', + 'description' => 'Migration with the requested ID could not be found. Please verify that the provided ID is correct and try again.', 'code' => 404, ], Exception::MIGRATION_ALREADY_EXISTS => [ 'name' => Exception::MIGRATION_ALREADY_EXISTS, - 'description' => 'Migration with the requested ID already exists.', + 'description' => 'Migration with the requested ID already exists. Try again with a different ID.', 'code' => 409, ], Exception::MIGRATION_IN_PROGRESS => [ 'name' => Exception::MIGRATION_IN_PROGRESS, - 'description' => 'Migration is already in progress.', + 'description' => 'Migration is already in progress. You can check the status of the migration in your Appwrite Console\'s "Settings" > "Migrations".', 'code' => 409, ], ]; diff --git a/app/config/services.php b/app/config/services.php index 9709abd1a6..3700af659a 100644 --- a/app/config/services.php +++ b/app/config/services.php @@ -189,7 +189,7 @@ return [ 'proxy' => [ 'key' => 'proxy', 'name' => 'Proxy', - 'subtitle' => 'The Proxy Service allows you configure actions for your domains beyond DNS configuration.', + 'subtitle' => 'The Proxy Service allows you to configure actions for your domains beyond DNS configuration.', 'description' => '/docs/services/proxy.md', 'controller' => 'api/proxy.php', 'sdk' => true, @@ -241,7 +241,7 @@ return [ 'migrations' => [ 'key' => 'migrations', 'name' => 'Migrations', - 'subtitle' => 'The Migrations service allows you to migrate third-party data to your Appwrite server.', + 'subtitle' => 'The Migrations service allows you to migrate third-party data to your Appwrite project.', 'description' => '/docs/services/migrations.md', 'controller' => 'api/migrations.php', 'sdk' => true, diff --git a/app/config/variables.php b/app/config/variables.php index 4187dc69bd..b1e6a508f9 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -116,7 +116,7 @@ return [ ], [ 'name' => '_APP_CONSOLE_ROOT_SESSION', - 'description' => 'Domain policy for the Appwrite console session cookie. By default, set to \'disabled\', meaning the session cookie will be set to the domain of the Appwrite console (e.g. cloud.appwrite.io). When set to \'enabled\', the session cookie will be set to the registerable domain of the Appwrite server (e.g. appwrite.io).', + 'description' => 'Domain policy for the Appwrite console session cookie. By default, set to \'disabled\', meaning the session cookie will be set to the domain of the Appwrite console, for example, "cloud.appwrite.io". When set to \'enabled\', the session cookie will be set to the registerable domain of the Appwrite server, for example, "appwrite.io".', 'introduction' => '', 'default' => 'disabled', 'required' => false, @@ -161,7 +161,7 @@ return [ ], [ 'name' => '_APP_USAGE_STATS', - 'description' => 'This variable allows you to disable the collection and displaying of usage stats. This value is set to \'enabled\' by default, to disable the usage stats set the value to \'disabled\'. When disabled, it\'s recommended to turn off the Worker Usage container for better resource usage.', + 'description' => 'This variable allows you to disable the collection and displaying of usage stats. This value is set to \'enabled\' by default, to disable the usage stats set the value to \'disabled\'. When disabled, it\'s recommended to turn off the Worker Usage container to reduce resource usage.', 'introduction' => '0.7.0', 'default' => 'enabled', 'required' => false, @@ -170,7 +170,7 @@ return [ ], [ 'name' => '_APP_LOGGING_PROVIDER', - 'description' => 'This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, to enable the logger set the value to one of \'sentry\', \'raygun\', \'appSignal\', \'logOwl\'', + 'description' => 'This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, set the value to one of \'sentry\', \'raygun\', \'appSignal\', \'logOwl\' to enable the logger.', 'introduction' => '0.12.0', 'default' => '', 'required' => false, @@ -823,7 +823,7 @@ return [ ], [ 'name' => '_APP_FUNCTIONS_RUNTIMES_NETWORK', - 'description' => 'The docker network used for communication between the executor and runtimes. Change this if you have altered the default network names.', + 'description' => 'The docker network used for communication between the executor and runtimes.', 'introduction' => '1.2.0', 'default' => 'runtimes', 'required' => false, @@ -865,7 +865,7 @@ return [ 'variables' => [ [ 'name' => '_APP_VCS_GITHUB_APP_NAME', - 'description' => 'Name of your GitHub app. You can find it in URL of your GitHub application.', + 'description' => 'Name of your GitHub app. This value should be set to your GitHub application\'s URL.', 'introduction' => '1.4.0', 'default' => '', 'required' => false, diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 0ef4c002c2..6102c330bb 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -395,8 +395,8 @@ App::get('/v1/account/sessions/oauth2/:provider') ->label('abuse-limit', 50) ->label('abuse-key', 'ip:{ip}') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('providers'), fn($node) => (!$node['mock'])))) . '.') - ->param('success', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) - ->param('failure', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) + ->param('success', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) + ->param('failure', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) ->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) ->inject('request') ->inject('response') @@ -454,7 +454,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId') ->label('docs', false) ->param('projectId', '', new Text(1024), 'Project ID.') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(2048, 0), 'OAuth2 code.', true) + ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->param('state', '', new Text(2048), 'Login state params.', true) ->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true) ->param('error_description', '', new Text(2048, 0), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) @@ -487,7 +487,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') ->label('docs', false) ->param('projectId', '', new Text(1024), 'Project ID.') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(2048, 0), 'OAuth2 code.', true) + ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->param('state', '', new Text(2048), 'Login state params.', true) ->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true) ->param('error_description', '', new Text(2048, 0), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) @@ -526,7 +526,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->label('usage.metric', 'sessions.{scope}.requests.create') ->label('usage.params', ['provider:{request.provider}']) ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(2048, 0), 'OAuth2 code.', true) + ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->param('state', '', new Text(2048), 'OAuth2 state params.', true) ->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true) ->param('error_description', '', new Text(2048, 0), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) diff --git a/app/controllers/api/console.php b/app/controllers/api/console.php index 87af828cae..1b51aac2b1 100644 --- a/app/controllers/api/console.php +++ b/app/controllers/api/console.php @@ -63,7 +63,7 @@ App::post('/v1/console/assistant') ->label('sdk.response.type', Response::CONTENT_TYPE_TEXT) ->label('abuse-limit', 15) ->label('abuse-key', 'userId:{userId}') - ->param('prompt', '', new Text(2000), 'Prompt') + ->param('prompt', '', new Text(2000), 'Prompt. A string containing questions asked to the AI assistant.') ->inject('response') ->action(function (string $prompt, Response $response) { $ch = curl_init('http://appwrite-assistant:3003/'); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 6711bf06ca..bf66e9fd8e 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -387,7 +387,7 @@ App::post('/v1/databases') ->label('sdk.response.model', Response::MODEL_DATABASE) // Model for database needs to be created ->param('databaseId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Database name. Max length: 128 chars.') - ->param('enabled', true, new Boolean(), 'Is database enabled?', true) + ->param('enabled', true, new Boolean(), 'Is the database enabled? When set to \'disabled\', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled.', true) ->inject('response') ->inject('dbForProject') ->inject('events') @@ -624,7 +624,7 @@ App::put('/v1/databases/:databaseId') ->label('sdk.response.model', Response::MODEL_DATABASE) ->param('databaseId', '', new UID(), 'Database ID.') ->param('name', null, new Text(128), 'Database name. Max length: 128 chars.') - ->param('enabled', true, new Boolean(), 'Is database enabled?', true) + ->param('enabled', true, new Boolean(), 'Is database enabled? When set to \'disabled\', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled.', true) ->inject('response') ->inject('dbForProject') ->inject('events') @@ -718,7 +718,7 @@ App::post('/v1/databases/:databaseId/collections') ->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](/docs/permissions).', true) ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](/docs/permissions).', true) - ->param('enabled', true, new Boolean(), 'Is collection enabled?', true) + ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) ->inject('response') ->inject('dbForProject') ->inject('mode') @@ -979,7 +979,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](/docs/permissions).', true) ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](/docs/permissions).', true) - ->param('enabled', true, new Boolean(), 'Is collection enabled?', true) + ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) ->inject('response') ->inject('dbForProject') ->inject('mode') diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index c089b8be9e..a286b4f567 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -135,23 +135,23 @@ App::post('/v1/functions') ->param('functionId', '', new CustomId(), 'Function ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Function name. Max length: 128 chars.') ->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.') - ->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true) + ->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with execution permissions. By default no user is granted with any execute permissions. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true) ->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true) ->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true) ->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true) - ->param('enabled', true, new Boolean(), 'Is function enabled?', true) - ->param('logging', true, new Boolean(), 'Do executions get logged?', true) - ->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File.', true) + ->param('enabled', true, new Boolean(), 'Is function enabled? When set to \'disabled\', users cannot access the function but Server SDKs with and API key can still access the function. No data is lost when this is toggled.', true) + ->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true) + ->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true) ->param('commands', '', new Text(8192, 0), 'Build Commands.', true) - ->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for vcs deployment.', true) - ->param('providerRepositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function', true) - ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function', true) - ->param('providerSilentMode', false, new Boolean(), 'Is VCS connection in silent mode for the repo linked to the function?', true) - ->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo', true) - ->param('templateRepository', '', new Text(128, 0), 'Repository name of the template', true) - ->param('templateOwner', '', new Text(128, 0), 'Owner name of the template', true) - ->param('templateRootDirectory', '', new Text(128, 0), 'Path to function code in the template repo', true) - ->param('templateBranch', '', new Text(128, 0), 'Branch of template repo with the code', true) + ->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for VCS (Version Control System) deployment.', true) + ->param('providerRepositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function.', true) + ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function.', true) + ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true) + ->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true) + ->param('templateRepository', '', new Text(128, 0), 'Repository name of the template.', true) + ->param('templateOwner', '', new Text(128, 0), 'The name of the owner of the template.', true) + ->param('templateRootDirectory', '', new Text(128, 0), 'Path to function code in the template repo.', true) + ->param('templateBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function template.', true) ->inject('request') ->inject('response') ->inject('dbForProject') @@ -661,19 +661,19 @@ App::put('/v1/functions/:functionId') ->param('functionId', '', new UID(), 'Function ID.') ->param('name', '', new Text(128), 'Function name. Max length: 128 chars.') ->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.') - ->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true) + ->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with execution permissions. By default no user is granted with any execute permissions. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true) ->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true) ->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true) ->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true) - ->param('enabled', true, new Boolean(), 'Is function enabled?', true) - ->param('logging', true, new Boolean(), 'Do executions get logged?', true) - ->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File.', true) + ->param('enabled', true, new Boolean(), 'Is function enabled? When set to \'disabled\', users cannot access the function but Server SDKs with and API key can still access the function. No data is lost when this is toggled.', true) + ->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true) + ->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true) ->param('commands', '', new Text(8192, 0), 'Build Commands.', true) - ->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for vcs deployment.', true) + ->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for VCS (Version Controle System) deployment.', true) ->param('providerRepositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function', true) ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function', true) - ->param('providerSilentMode', false, new Boolean(), 'Is VCS connection in silent mode for the repo linked to the function?', true) - ->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo', true) + ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true) + ->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true) ->inject('request') ->inject('response') ->inject('dbForProject') diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index d981c36ec0..3ae2d65d36 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -625,7 +625,7 @@ App::get('/v1/migrations/firebase/redirect') ->groups(['api', 'migrations']) ->label('scope', 'public') ->label('error', __DIR__ . '/../../views/general/error.phtml') - ->param('code', '', new Text(2048), 'OAuth2 code.', true) + ->param('code', '', new Text(2048), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->inject('user') ->inject('project') ->inject('request') @@ -859,12 +859,12 @@ App::get('/v1/migrations/supabase/report') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_MIGRATION_REPORT) ->param('resources', [], new ArrayList(new WhiteList(Supabase::getSupportedResources(), true)), 'List of resources to migrate') - ->param('endpoint', '', new URL(), 'Source\'s Supabase Endpoint') - ->param('apiKey', '', new Text(512), 'Source\'s API Key') - ->param('databaseHost', '', new Text(512), 'Source\'s Database Host') - ->param('username', '', new Text(512), 'Source\'s Database Username') - ->param('password', '', new Text(512), 'Source\'s Database Password') - ->param('port', 5432, new Integer(true), 'Source\'s Database Port', true) + ->param('endpoint', '', new URL(), 'Source\'s Supabase Endpoint.') + ->param('apiKey', '', new Text(512), 'Source\'s API Key.') + ->param('databaseHost', '', new Text(512), 'Source\'s Database Host.') + ->param('username', '', new Text(512), 'Source\'s Database Username.') + ->param('password', '', new Text(512), 'Source\'s Database Password.') + ->param('port', 5432, new Integer(true), 'Source\'s Database Port.', true) ->inject('response') ->inject('dbForProject') ->action(function (array $resources, string $endpoint, string $apiKey, string $databaseHost, string $username, string $password, int $port, Response $response) { @@ -890,14 +890,14 @@ App::get('/v1/migrations/nhost/report') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_MIGRATION_REPORT) - ->param('resources', [], new ArrayList(new WhiteList(NHost::getSupportedResources())), 'List of resources to migrate') - ->param('subdomain', '', new Text(512), 'Source\'s Subdomain') - ->param('region', '', new Text(512), 'Source\'s Region') - ->param('adminSecret', '', new Text(512), 'Source\'s Admin Secret') - ->param('database', '', new Text(512), 'Source\'s Database Name') - ->param('username', '', new Text(512), 'Source\'s Database Username') - ->param('password', '', new Text(512), 'Source\'s Database Password') - ->param('port', 5432, new Integer(true), 'Source\'s Database Port', true) + ->param('resources', [], new ArrayList(new WhiteList(NHost::getSupportedResources())), 'List of resources to migrate.') + ->param('subdomain', '', new Text(512), 'Source\'s Subdomain.') + ->param('region', '', new Text(512), 'Source\'s Region.') + ->param('adminSecret', '', new Text(512), 'Source\'s Admin Secret.') + ->param('database', '', new Text(512), 'Source\'s Database Name.') + ->param('username', '', new Text(512), 'Source\'s Database Username.') + ->param('password', '', new Text(512), 'Source\'s Database Password.') + ->param('port', 5432, new Integer(true), 'Source\'s Database Port.', true) ->inject('response') ->action(function (array $resources, string $subdomain, string $region, string $adminSecret, string $database, string $username, string $password, int $port, Response $response) { try { diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 226c947d5a..8cad0aaf8f 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -64,8 +64,8 @@ App::post('/v1/storage/buckets') ->param('name', '', new Text(128), 'Bucket name') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](/docs/permissions).', true) ->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](/docs/permissions).', true) - ->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true) - ->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self-hosted setups you can change the max limit by changing the `_APP_STORAGE_LIMIT` environment variable. [Learn more about storage environment variables](/docs/environment-variables#storage)', true) + ->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true) + ->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true) ->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true) ->param('compression', COMPRESSION_TYPE_NONE, new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . COMPRESSION_TYPE_NONE . ', [' . COMPRESSION_TYPE_GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . COMPRESSION_TYPE_ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true) ->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true) @@ -238,8 +238,8 @@ App::put('/v1/storage/buckets/:bucketId') ->param('name', null, new Text(128), 'Bucket name', false) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](/docs/permissions).', true) ->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](/docs/permissions).', true) - ->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true) - ->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self hosted version you can change the limit by changing _APP_STORAGE_LIMIT environment variable. [Learn more about storage environment variables](/docs/environment-variables#storage)', true) + ->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true) + ->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true) ->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true) ->param('compression', COMPRESSION_TYPE_NONE, new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . COMPRESSION_TYPE_NONE . ', [' . COMPRESSION_TYPE_GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . COMPRESSION_TYPE_ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true) ->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index ff1853f3f9..5477ca356c 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -269,7 +269,7 @@ App::get('/v1/vcs/github/callback') ->param('installation_id', '', new Text(256, 0), 'GitHub installation ID', true) ->param('setup_action', '', new Text(256, 0), 'GitHub setup actuon type', true) ->param('state', '', new Text(2048), 'GitHub state. Contains info sent when starting authorization flow.', true) - ->param('code', '', new Text(2048, 0), 'OAuth2 code.', true) + ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->inject('gitHub') ->inject('user') ->inject('project') diff --git a/app/workers/builds.php b/app/workers/builds.php index a531e5b778..1d9a0d6258 100644 --- a/app/workers/builds.php +++ b/app/workers/builds.php @@ -87,7 +87,7 @@ class BuildsV1 extends Worker } if (empty($deployment->getAttribute('entrypoint', ''))) { - throw new Exception('Function entrypoint is not configured. Please specify it in function settings or when making deployment.', 500); + throw new Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 500); } $runtimes = Config::getParam('runtimes', []); diff --git a/docs/references/functions/create-deployment.md b/docs/references/functions/create-deployment.md index 7cae5989ae..c9dc6e3a7b 100644 --- a/docs/references/functions/create-deployment.md +++ b/docs/references/functions/create-deployment.md @@ -2,4 +2,4 @@ Create a new function code deployment. Use this endpoint to upload a new version This endpoint accepts a tar.gz file compressed with your code. Make sure to include any dependencies your code has within the compressed file. You can learn more about code packaging in the [Appwrite Cloud Functions tutorial](/docs/functions). -Use the "command" param to set the entry point used to execute your code. \ No newline at end of file +Use the "command" param to set the entrypoint used to execute your code. \ No newline at end of file diff --git a/docs/references/functions/create-variable.md b/docs/references/functions/create-variable.md index d5c11ff2ab..40fabd75a8 100644 --- a/docs/references/functions/create-variable.md +++ b/docs/references/functions/create-variable.md @@ -1 +1 @@ -Create a new function variable. These variables can be accessed within function as environment variable. \ No newline at end of file +Create a new function environment variable. These variables can be accessed in the function at runtime as environment variables. \ No newline at end of file diff --git a/docs/references/project/create-variable.md b/docs/references/project/create-variable.md index d49135a006..2bbee5bf99 100644 --- a/docs/references/project/create-variable.md +++ b/docs/references/project/create-variable.md @@ -1 +1 @@ -Create a new project variable. \ No newline at end of file +Create a new project variable. This variable will be accessible in all Appwrite Functions at runtime. \ No newline at end of file diff --git a/docs/references/project/delete-variable.md b/docs/references/project/delete-variable.md index 42f36dccfe..9be15f83ca 100644 --- a/docs/references/project/delete-variable.md +++ b/docs/references/project/delete-variable.md @@ -1 +1 @@ -Delete a project variable by its unique ID. \ No newline at end of file +Delete a project variable by its unique ID. \ No newline at end of file diff --git a/docs/references/project/list-variables.md b/docs/references/project/list-variables.md index 0c540cf482..fbe191178a 100644 --- a/docs/references/project/list-variables.md +++ b/docs/references/project/list-variables.md @@ -1 +1 @@ -Get a list of all project variables. \ No newline at end of file +Get a list of all project variables. These variables will be accessible in all Appwrite Functions at runtime. \ No newline at end of file diff --git a/docs/references/project/update-variable.md b/docs/references/project/update-variable.md index 9ba1d854d3..603622b2c7 100644 --- a/docs/references/project/update-variable.md +++ b/docs/references/project/update-variable.md @@ -1 +1 @@ -Update project variable by its unique ID. \ No newline at end of file +Update project variable by its unique ID. This variable will be accessible in all Appwrite Functions at runtime. \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php index ae3e283378..e634c88f3a 100644 --- a/src/Appwrite/Utopia/Response/Model/Collection.php +++ b/src/Appwrite/Utopia/Response/Model/Collection.php @@ -49,7 +49,7 @@ class Collection extends Model ]) ->addRule('enabled', [ 'type' => self::TYPE_BOOLEAN, - 'description' => 'Collection enabled.', + 'description' => 'Collection enabled. Can be \'enabled\' or \'disabled\'. When disabled, the collection is inaccessible to users, but remains accessible to Server SDKs using API keys.', 'default' => true, 'example' => false, ]) diff --git a/src/Appwrite/Utopia/Response/Model/Database.php b/src/Appwrite/Utopia/Response/Model/Database.php index bd9ae4625c..90b4ac8cb4 100644 --- a/src/Appwrite/Utopia/Response/Model/Database.php +++ b/src/Appwrite/Utopia/Response/Model/Database.php @@ -36,7 +36,7 @@ class Database extends Model ]) ->addRule('enabled', [ 'type' => self::TYPE_BOOLEAN, - 'description' => 'Database enabled.', + 'description' => 'If database is enabled. Can be \'enabled\' or \'disabled\'. When disabled, the database is inaccessible to users, but remains accessible to Server SDKs using API keys.', 'default' => true, 'example' => false, ]) diff --git a/src/Appwrite/Utopia/Response/Model/Func.php b/src/Appwrite/Utopia/Response/Model/Func.php index f583d4ca9d..e32735b38e 100644 --- a/src/Appwrite/Utopia/Response/Model/Func.php +++ b/src/Appwrite/Utopia/Response/Model/Func.php @@ -51,13 +51,13 @@ class Func extends Model ]) ->addRule('live', [ 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is function live (deployed with latest config)?', + 'description' => 'Is the function deployed with the latest configuration? This is set to false if you\'ve changed an environment variables, entrypoint, commands, or other settings that needs redeploy to be applied. When the value is false, redeploy the function to update it with the latest configuration.', 'default' => true, 'example' => false, ]) ->addRule('logging', [ 'type' => self::TYPE_BOOLEAN, - 'description' => 'Function logging.', + 'description' => 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', 'default' => true, 'example' => false, ])