Merge remote-tracking branch 'origin/1.5.x' into feat-rc-sdks
# Conflicts: # app/config/specs/open-api3-1.5.x-client.json # app/config/specs/open-api3-1.5.x-console.json # app/config/specs/open-api3-1.5.x-server.json # app/config/specs/open-api3-latest-client.json # app/config/specs/open-api3-latest-console.json # app/config/specs/open-api3-latest-server.json # app/config/specs/swagger2-1.5.x-client.json # app/config/specs/swagger2-1.5.x-console.json # app/config/specs/swagger2-1.5.x-server.json # app/config/specs/swagger2-latest-client.json # app/config/specs/swagger2-latest-console.json # app/config/specs/swagger2-latest-server.json # composer.lock
This commit is contained in:
commit
cd554aa17f
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
|
@ -141,7 +141,7 @@ jobs:
|
|||
run: |
|
||||
docker load --input /tmp/${{ env.IMAGE }}.tar
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
sleep 25
|
||||
|
||||
- name: Run ${{matrix.service}} Tests
|
||||
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"settings.direction": "ltr",
|
||||
"emails.sender": "El equipo de %s",
|
||||
"emails.verification.subject": "Verificación de cuenta",
|
||||
"emails.verification.hello": "Hola, {{name}}",
|
||||
"emails.verification.hello": "Hola, {{name}}.",
|
||||
"emails.verification.body": "Haz clic en este enlace para verificar tu correo:",
|
||||
"emails.verification.footer": "Si no has solicitado verificar este correo, puedes ignorar este mensaje.",
|
||||
"emails.verification.thanks": "Gracias.",
|
||||
|
@ -16,7 +16,7 @@
|
|||
"emails.magicSession.thanks": "Gracias.",
|
||||
"emails.magicSession.signature": "El equipo de {{project}}",
|
||||
"emails.recovery.subject": "Restablecer contraseña",
|
||||
"emails.recovery.hello": "Hola, {{name}}",
|
||||
"emails.recovery.hello": "Hola, {{name}}.",
|
||||
"emails.recovery.body": "Haz clic en este enlace para restablecer la contraseña de {{project}}:",
|
||||
"emails.recovery.footer": "Si no has solicitado restablecer la contraseña, puedes ignorar este mensaje.",
|
||||
"emails.recovery.thanks": "Gracias.",
|
||||
|
@ -236,10 +236,10 @@
|
|||
"emails.magicSession.securityPhrase": "La frase de seguridad para este correo electrónico es {{phrase}}. Puedes confiar en este correo electrónico si esta frase coincide con la frase que se muestra durante el inicio de sesión.",
|
||||
"emails.magicSession.optionUrl": "Si no puedes iniciar sesión utilizando el botón anterior, visita el siguiente enlace:",
|
||||
"emails.otpSession.subject": "Inicio de sesión en {{project}}",
|
||||
"emails.otpSession.hello": "Hola,",
|
||||
"emails.otpSession.hello": "Hola",
|
||||
"emails.otpSession.description": "Ingrese el siguiente código de verificación cuando se le solicite para iniciar sesión de forma segura en su cuenta de {{project}}. Expirará en 15 minutos.",
|
||||
"emails.otpSession.clientInfo": "Este inicio de sesión fue solicitado usando {{agentClient}} en {{agentDevice}} {{agentOs}}. Si no solicitaste el inicio de sesión, puedes ignorar este correo electrónico de forma segura.",
|
||||
"emails.otpSession.securityPhrase": "La frase de seguridad para este correo electrónico es {{phrase}}. Puedes confiar en este correo si esta frase coincide con la frase mostrada durante el inicio de sesión.",
|
||||
"emails.otpSession.thanks": "Gracias,",
|
||||
"emails.otpSession.signature": "equipo {{project}}"
|
||||
}
|
||||
"emails.otpSession.thanks": "Gracias.",
|
||||
"emails.otpSession.signature": "El equipo de {{project}}"
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
Subproject commit c72ba12e479b0d3d9b3f4e48e01625c12a38fd7f
|
||||
Subproject commit f196bcfb485adfb36324aabf32a3449471319bbd
|
|
@ -330,7 +330,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
|||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('scope', 'sessions.write')
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.hideServer', true)
|
||||
->label('sdk.hide', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'createOAuth2Session')
|
||||
->label('sdk.description', '/docs/references/account/create-session-oauth2.md')
|
||||
|
@ -400,7 +400,6 @@ App::get('/v1/account/tokens/oauth2/:provider')
|
|||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('scope', 'sessions.write')
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.hideServer', true)
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'createOAuth2Token')
|
||||
->label('sdk.description', '/docs/references/account/create-token-oauth2.md')
|
||||
|
@ -1635,8 +1634,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
|||
};
|
||||
|
||||
App::put('/v1/account/sessions/magic-url')
|
||||
->alias('/v1/account/sessions/phone')
|
||||
->desc('Create session (deprecated)')
|
||||
->desc('Update magic URL session')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'sessions.write')
|
||||
|
@ -1644,8 +1642,39 @@ App::put('/v1/account/sessions/magic-url')
|
|||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.deprecated', true)
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', ['updateMagicURLSession', 'updatePhoneSession'])
|
||||
->label('sdk.method', 'updateMagicURLSession')
|
||||
->label('sdk.description', '/docs/references/account/create-session.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_SESSION)
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'ip:{ip},userId:{param-userId}')
|
||||
->param('userId', '', new CustomId(), 'User 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('secret', '', new Text(256), 'Valid verification token.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->action($createSession);
|
||||
|
||||
App::put('/v1/account/sessions/phone')
|
||||
->desc('Update phone session')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'sessions.write')
|
||||
->label('audits.event', 'session.create')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.deprecated', true)
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updatePhoneSession')
|
||||
->label('sdk.description', '/docs/references/account/create-session.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
|
@ -2671,7 +2700,7 @@ App::delete('/v1/account/sessions/:sessionId')
|
|||
});
|
||||
|
||||
App::patch('/v1/account/sessions/:sessionId')
|
||||
->desc('Update (or renew) a session')
|
||||
->desc('Update (or renew) session')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].update')
|
||||
|
@ -2965,7 +2994,7 @@ App::post('/v1/account/recovery')
|
|||
|
||||
$queueForMails
|
||||
->setRecipient($profile->getAttribute('email', ''))
|
||||
->setName($profile->getAttribute('name'))
|
||||
->setName($profile->getAttribute('name', ''))
|
||||
->setBody($body)
|
||||
->setVariables($emailVariables)
|
||||
->setSubject($subject)
|
||||
|
@ -3918,11 +3947,11 @@ App::put('/v1/account/mfa/challenge')
|
|||
};
|
||||
|
||||
if (!$success && $provider === 'totp') {
|
||||
$backups = $user->getAttribute('mfaBackups', []);
|
||||
$backups = $user->getAttribute('totpBackup', []);
|
||||
if (in_array($otp, $backups)) {
|
||||
$success = true;
|
||||
$backups = array_diff($backups, [$otp]);
|
||||
$user->setAttribute('mfaBackups', $backups);
|
||||
$user->setAttribute('totpBackup', $backups);
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
}
|
||||
}
|
||||
|
@ -3940,6 +3969,9 @@ App::put('/v1/account/mfa/challenge')
|
|||
|
||||
$dbForProject->updateDocument('sessions', $sessionId, $session->setAttribute('factors', $provider, Document::SET_TYPE_APPEND));
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId());
|
||||
|
||||
$response->dynamic($session, Response::MODEL_SESSION);
|
||||
});
|
||||
|
||||
|
@ -3992,7 +4024,7 @@ App::delete('/v1/account')
|
|||
});
|
||||
|
||||
App::post('/v1/account/targets/push')
|
||||
->desc('Create a push target')
|
||||
->desc('Create push target')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'targets.write')
|
||||
->label('audits.event', 'target.create')
|
||||
|
@ -4065,7 +4097,7 @@ App::post('/v1/account/targets/push')
|
|||
});
|
||||
|
||||
App::put('/v1/account/targets/:targetId/push')
|
||||
->desc('Update a push target')
|
||||
->desc('Update push target')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'targets.write')
|
||||
->label('audits.event', 'target.update')
|
||||
|
@ -4120,7 +4152,7 @@ App::put('/v1/account/targets/:targetId/push')
|
|||
});
|
||||
|
||||
App::delete('/v1/account/targets/:targetId/push')
|
||||
->desc('Delete a push target')
|
||||
->desc('Delete push target')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'targets.write')
|
||||
->label('audits.event', 'target.delete')
|
||||
|
|
|
@ -1616,6 +1616,54 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati
|
|||
$key ??= $relatedCollectionId;
|
||||
$twoWayKey ??= $collectionId;
|
||||
|
||||
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
|
||||
$collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception(Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$relatedCollectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId);
|
||||
$relatedCollection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollectionDocument->getInternalId());
|
||||
|
||||
if ($relatedCollection->isEmpty()) {
|
||||
throw new Exception(Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$attributes = $collection->getAttribute('attributes', []);
|
||||
/** @var Document[] $attributes */
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute->getAttribute('type') !== Database::VAR_RELATIONSHIP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\strtolower($attribute->getId()) === \strtolower($key)) {
|
||||
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
if (
|
||||
\strtolower($attribute->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) &&
|
||||
$attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId()
|
||||
) {
|
||||
// Console should provide a unique twoWayKey input!
|
||||
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.');
|
||||
}
|
||||
|
||||
if (
|
||||
$type === Database::RELATION_MANY_TO_MANY &&
|
||||
$attribute->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY &&
|
||||
$attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId()
|
||||
) {
|
||||
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Creating more than one "manyToMany" relationship on the same collection is currently not permitted.');
|
||||
}
|
||||
}
|
||||
|
||||
$attribute = createAttribute(
|
||||
$databaseId,
|
||||
$collectionId,
|
||||
|
@ -2356,7 +2404,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
|
|||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
|
||||
->param('key', null, new Key(), 'Index Key.')
|
||||
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL]), 'Index type.')
|
||||
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.')
|
||||
->param('attributes', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attributes to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' attributes are allowed, each 32 characters long.')
|
||||
->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true)
|
||||
->inject('response')
|
||||
|
@ -3042,6 +3090,33 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
$processDocument($collection, $document);
|
||||
}
|
||||
|
||||
$select = \array_reduce($queries, function ($result, $query) {
|
||||
return $result || ($query->getMethod() === Query::TYPE_SELECT);
|
||||
}, false);
|
||||
|
||||
// Check if the SELECT query includes $databaseId and $collectionId
|
||||
$hasDatabaseId = false;
|
||||
$hasCollectionId = false;
|
||||
if ($select) {
|
||||
$hasDatabaseId = \array_reduce($queries, function ($result, $query) {
|
||||
return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$databaseId', $query->getValues()));
|
||||
}, false);
|
||||
$hasCollectionId = \array_reduce($queries, function ($result, $query) {
|
||||
return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$collectionId', $query->getValues()));
|
||||
}, false);
|
||||
}
|
||||
|
||||
if ($select) {
|
||||
foreach ($documents as $document) {
|
||||
if (!$hasDatabaseId) {
|
||||
$document->removeAttribute('$databaseId');
|
||||
}
|
||||
if (!$hasCollectionId) {
|
||||
$document->removeAttribute('$collectionId');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $total,
|
||||
'documents' => $documents,
|
||||
|
@ -3587,7 +3662,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
|||
});
|
||||
|
||||
App::get('/v1/databases/usage')
|
||||
->desc('Get usage stats for the database')
|
||||
->desc('Get databases usage stats')
|
||||
->groups(['api', 'database', 'usage'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
|
@ -3665,7 +3740,7 @@ App::get('/v1/databases/usage')
|
|||
});
|
||||
|
||||
App::get('/v1/databases/:databaseId/usage')
|
||||
->desc('Get usage stats for the database')
|
||||
->desc('Get database usage stats')
|
||||
->groups(['api', 'database', 'usage'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
|
@ -3749,7 +3824,7 @@ App::get('/v1/databases/:databaseId/usage')
|
|||
|
||||
App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
|
||||
->alias('/v1/database/:collectionId/usage', ['databaseId' => 'default'])
|
||||
->desc('Get usage stats for a collection')
|
||||
->desc('Get collection usage stats')
|
||||
->groups(['api', 'database', 'usage'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
|
|
|
@ -420,17 +420,23 @@ App::get('/v1/functions/runtimes')
|
|||
->label('sdk.response.model', Response::MODEL_RUNTIME_LIST)
|
||||
->inject('response')
|
||||
->action(function (Response $response) {
|
||||
|
||||
$runtimes = Config::getParam('runtimes');
|
||||
|
||||
$runtimes = array_map(function ($key) use ($runtimes) {
|
||||
$allowList = \array_filter(\explode(',', App::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
|
||||
|
||||
$allowed = [];
|
||||
foreach ($runtimes as $key => $runtime) {
|
||||
if (!empty($allowList) && !\in_array($key, $allowList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$runtimes[$key]['$id'] = $key;
|
||||
return $runtimes[$key];
|
||||
}, array_keys($runtimes));
|
||||
$allowed[] = $runtimes[$key];
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => count($runtimes),
|
||||
'runtimes' => $runtimes
|
||||
'total' => count($allowed),
|
||||
'runtimes' => $allowed
|
||||
]), Response::MODEL_RUNTIME_LIST);
|
||||
});
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ App::post('/v1/messaging/providers/sendgrid')
|
|||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createSendgridProvider')
|
||||
->label('sdk.description', '/docs/references/messaging/create-sengrid-provider.md')
|
||||
->label('sdk.description', '/docs/references/messaging/create-sendgrid-provider.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_PROVIDER)
|
||||
|
@ -1924,7 +1924,7 @@ App::delete('/v1/messaging/providers/:providerId')
|
|||
});
|
||||
|
||||
App::post('/v1/messaging/topics')
|
||||
->desc('Create a topic')
|
||||
->desc('Create topic')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'topic.create')
|
||||
->label('audits.resource', 'topic/{response.$id}')
|
||||
|
@ -2107,7 +2107,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
|
|||
});
|
||||
|
||||
App::get('/v1/messaging/topics/:topicId')
|
||||
->desc('Get a topic')
|
||||
->desc('Get topic')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'topics.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
|
@ -2134,7 +2134,7 @@ App::get('/v1/messaging/topics/:topicId')
|
|||
});
|
||||
|
||||
App::patch('/v1/messaging/topics/:topicId')
|
||||
->desc('Update a topic')
|
||||
->desc('Update topic')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'topic.update')
|
||||
->label('audits.resource', 'topic/{response.$id}')
|
||||
|
@ -2178,7 +2178,7 @@ App::patch('/v1/messaging/topics/:topicId')
|
|||
});
|
||||
|
||||
App::delete('/v1/messaging/topics/:topicId')
|
||||
->desc('Delete a topic')
|
||||
->desc('Delete topic')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'topic.delete')
|
||||
->label('audits.resource', 'topic/{request.$topicId}')
|
||||
|
@ -2218,7 +2218,7 @@ App::delete('/v1/messaging/topics/:topicId')
|
|||
});
|
||||
|
||||
App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||
->desc('Create a subscriber')
|
||||
->desc('Create subscriber')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'subscriber.create')
|
||||
->label('audits.resource', 'subscriber/{response.$id}')
|
||||
|
@ -2477,7 +2477,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
|||
});
|
||||
|
||||
App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->desc('Get a subscriber')
|
||||
->desc('Get subscriber')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'subscribers.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
|
@ -2516,7 +2516,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
|||
});
|
||||
|
||||
App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->desc('Delete a subscriber')
|
||||
->desc('Delete subscriber')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'subscriber.delete')
|
||||
->label('audits.resource', 'subscriber/{request.$subscriberId}')
|
||||
|
@ -2575,7 +2575,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
|||
});
|
||||
|
||||
App::post('/v1/messaging/messages/email')
|
||||
->desc('Create an email')
|
||||
->desc('Create email')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.create')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
|
@ -2719,7 +2719,7 @@ App::post('/v1/messaging/messages/email')
|
|||
});
|
||||
|
||||
App::post('/v1/messaging/messages/sms')
|
||||
->desc('Create an SMS')
|
||||
->desc('Create SMS')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.create')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
|
@ -2827,7 +2827,7 @@ App::post('/v1/messaging/messages/sms')
|
|||
});
|
||||
|
||||
App::post('/v1/messaging/messages/push')
|
||||
->desc('Create a push notification')
|
||||
->desc('Create push notification')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.create')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
|
@ -3190,7 +3190,7 @@ App::get('/v1/messaging/messages/:messageId/targets')
|
|||
});
|
||||
|
||||
App::get('/v1/messaging/messages/:messageId')
|
||||
->desc('Get a message')
|
||||
->desc('Get message')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'messages.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
|
@ -3214,7 +3214,7 @@ App::get('/v1/messaging/messages/:messageId')
|
|||
});
|
||||
|
||||
App::patch('/v1/messaging/messages/email/:messageId')
|
||||
->desc('Update an email')
|
||||
->desc('Update email')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.update')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
|
@ -3352,7 +3352,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
|||
});
|
||||
|
||||
App::patch('/v1/messaging/messages/sms/:messageId')
|
||||
->desc('Update an SMS')
|
||||
->desc('Update SMS')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.update')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
|
@ -3470,7 +3470,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
|||
});
|
||||
|
||||
App::patch('/v1/messaging/messages/push/:messageId')
|
||||
->desc('Update a push notification')
|
||||
->desc('Update push notification')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.update')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
|
@ -3661,7 +3661,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
|||
});
|
||||
|
||||
App::delete('/v1/messaging/messages/:messageId')
|
||||
->desc('Delete a message')
|
||||
->desc('Delete message')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.delete')
|
||||
->label('audits.resource', 'message/{request.route.messageId}')
|
||||
|
|
|
@ -1038,7 +1038,7 @@ App::delete('/v1/migrations/:migrationId')
|
|||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'migrations')
|
||||
->label('sdk.method', 'delete')
|
||||
->label('sdk.description', '/docs/references/functions/delete-migration.md')
|
||||
->label('sdk.description', '/docs/references/migrations/delete-migration.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('migrationId', '', new UID(), 'Migration ID.')
|
||||
|
|
|
@ -18,7 +18,7 @@ use Utopia\Validator\Text;
|
|||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::get('/v1/project/usage')
|
||||
->desc('Get usage stats for a project')
|
||||
->desc('Get project usage stats')
|
||||
->groups(['api', 'usage'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
|
|
|
@ -1514,7 +1514,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
});
|
||||
|
||||
App::get('/v1/storage/usage')
|
||||
->desc('Get usage stats for storage')
|
||||
->desc('Get storage usage stats')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
|
@ -1593,7 +1593,7 @@ App::get('/v1/storage/usage')
|
|||
});
|
||||
|
||||
App::get('/v1/storage/:bucketId/usage')
|
||||
->desc('Get usage stats for storage bucket')
|
||||
->desc('Get bucket usage stats')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
|
|
|
@ -756,8 +756,20 @@ App::get('/v1/teams/:teamId/memberships')
|
|||
$memberships = array_map(function ($membership) use ($dbForProject, $team) {
|
||||
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
|
||||
$mfa = $user->getAttribute('mfa', false);
|
||||
|
||||
if ($mfa) {
|
||||
$totpEnabled = $user->getAttribute('totp', false) && $user->getAttribute('totpVerification', false);
|
||||
$emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false);
|
||||
$phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false);
|
||||
|
||||
if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) {
|
||||
$mfa = false;
|
||||
}
|
||||
}
|
||||
|
||||
$membership
|
||||
->setAttribute('mfa', $user->getAttribute('mfa'))
|
||||
->setAttribute('mfa', $mfa)
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $user->getAttribute('name'))
|
||||
->setAttribute('userEmail', $user->getAttribute('email'))
|
||||
|
@ -805,7 +817,20 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
|||
|
||||
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
|
||||
$mfa = $user->getAttribute('mfa', false);
|
||||
|
||||
if ($mfa) {
|
||||
$totpEnabled = $user->getAttribute('totp', false) && $user->getAttribute('totpVerification', false);
|
||||
$emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false);
|
||||
$phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false);
|
||||
|
||||
if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) {
|
||||
$mfa = false;
|
||||
}
|
||||
}
|
||||
|
||||
$membership
|
||||
->setAttribute('mfa', $mfa)
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $user->getAttribute('name'))
|
||||
->setAttribute('userEmail', $user->getAttribute('email'))
|
||||
|
|
|
@ -71,7 +71,14 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
|||
: ID::custom($userId);
|
||||
|
||||
if ($project->getAttribute('auths', [])['personalDataCheck'] ?? false) {
|
||||
$personalDataValidator = new PersonalData($userId, $email, $name, $phone);
|
||||
$personalDataValidator = new PersonalData(
|
||||
$userId,
|
||||
$email,
|
||||
$name,
|
||||
$phone,
|
||||
strict: false,
|
||||
allowEmpty: true
|
||||
);
|
||||
if (!$personalDataValidator->isValid($plaintextPassword)) {
|
||||
throw new Exception(Exception::USER_PASSWORD_PERSONAL_DATA);
|
||||
}
|
||||
|
@ -1599,30 +1606,20 @@ App::delete('/v1/users/:userId/mfa/:type')
|
|||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('type', null, new WhiteList(['totp']), 'Type of authenticator.')
|
||||
->param('otp', '', new Text(256), 'Valid verification token.')
|
||||
->inject('requestTimestamp')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $userId, string $type, string $otp, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $userId, string $type, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$success = match ($type) {
|
||||
'totp' => Challenge\TOTP::verify($user, $otp),
|
||||
default => false
|
||||
};
|
||||
|
||||
if (!$success) {
|
||||
throw new Exception(Exception::USER_INVALID_TOKEN);
|
||||
}
|
||||
|
||||
if (!$user->getAttribute('totp')) {
|
||||
throw new Exception(Exception::GENERAL_UNKNOWN, 'TOTP not added.');
|
||||
}
|
||||
if (!$user->getAttribute('totp')) {
|
||||
throw new Exception(Exception::GENERAL_UNKNOWN, 'TOTP not added.');
|
||||
}
|
||||
|
||||
$user
|
||||
->setAttribute('totp', false)
|
||||
|
@ -1978,7 +1975,7 @@ App::delete('/v1/users/identities/:identityId')
|
|||
});
|
||||
|
||||
App::get('/v1/users/usage')
|
||||
->desc('Get usage stats for the users API')
|
||||
->desc('Get users usage stats')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/logger": "0.3.*",
|
||||
"utopia-php/messaging": "0.10.*",
|
||||
"utopia-php/migration": "0.3.*",
|
||||
"utopia-php/migration": "0.4.*",
|
||||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.5.*",
|
||||
"utopia-php/pools": "0.4.*",
|
||||
|
|
28
composer.lock
generated
28
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2541976b03be9075d1fa17bd99c08eed",
|
||||
"content-hash": "c87d5177156cd31ba78e1047af962dbe",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
@ -65,16 +65,16 @@
|
|||
},
|
||||
{
|
||||
"name": "appwrite/appwrite",
|
||||
"version": "10.0.0",
|
||||
"version": "10.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-for-php.git",
|
||||
"reference": "461eedf4efd502dc905c3055f36f0e3583f67390"
|
||||
"reference": "da579af70723cfc117b5af84375bdef117e27312"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/461eedf4efd502dc905c3055f36f0e3583f67390",
|
||||
"reference": "461eedf4efd502dc905c3055f36f0e3583f67390",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/da579af70723cfc117b5af84375bdef117e27312",
|
||||
"reference": "da579af70723cfc117b5af84375bdef117e27312",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -99,10 +99,10 @@
|
|||
"support": {
|
||||
"email": "team@appwrite.io",
|
||||
"issues": "https://github.com/appwrite/sdk-for-php/issues",
|
||||
"source": "https://github.com/appwrite/sdk-for-php/tree/10.0.0",
|
||||
"source": "https://github.com/appwrite/sdk-for-php/tree/10.1.0",
|
||||
"url": "https://appwrite.io/support"
|
||||
},
|
||||
"time": "2023-09-07T23:28:31+00:00"
|
||||
"time": "2023-11-20T09:56:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "appwrite/php-clamav",
|
||||
|
@ -1962,20 +1962,20 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "0.3.6",
|
||||
"version": "0.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "f78273b38bade23db5866e5c7cb5f55427ba82af"
|
||||
"reference": "a72f27bd3dde68752fb185d306c4820e1b8d9657"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/f78273b38bade23db5866e5c7cb5f55427ba82af",
|
||||
"reference": "f78273b38bade23db5866e5c7cb5f55427ba82af",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/a72f27bd3dde68752fb185d306c4820e1b8d9657",
|
||||
"reference": "a72f27bd3dde68752fb185d306c4820e1b8d9657",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"appwrite/appwrite": "10.0.*",
|
||||
"appwrite/appwrite": "10.1.0",
|
||||
"php": "8.*",
|
||||
"utopia-php/cli": "0.*"
|
||||
},
|
||||
|
@ -2004,9 +2004,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.3.6"
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.4.0"
|
||||
},
|
||||
"time": "2023-11-02T15:13:03+00:00"
|
||||
"time": "2024-02-25T12:35:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/mongo",
|
||||
|
|
1
docs/references/account/add-authenticator.md
Normal file
1
docs/references/account/add-authenticator.md
Normal file
|
@ -0,0 +1 @@
|
|||
Add an authenticator app to be used as an MFA factor. Verify the authenticator using the [verify authenticator](/docs/references/cloud/client-web/account#verifyAuthenticator) method.
|
1
docs/references/account/create-2fa-challenge.md
Normal file
1
docs/references/account/create-2fa-challenge.md
Normal file
|
@ -0,0 +1 @@
|
|||
Initialize an MFA challenge of the specified factor. The factor must be available on the account.
|
1
docs/references/account/delete-mfa.md
Normal file
1
docs/references/account/delete-mfa.md
Normal file
|
@ -0,0 +1 @@
|
|||
Delete an authenticator for a user by ID.
|
1
docs/references/account/list-factors.md
Normal file
1
docs/references/account/list-factors.md
Normal file
|
@ -0,0 +1 @@
|
|||
List the factors available on the account to be used as a MFA challange.
|
1
docs/references/account/update-challenge.md
Normal file
1
docs/references/account/update-challenge.md
Normal file
|
@ -0,0 +1 @@
|
|||
Complete the MFA challenge by providing the one-time password.
|
1
docs/references/account/update-mfa.md
Normal file
1
docs/references/account/update-mfa.md
Normal file
|
@ -0,0 +1 @@
|
|||
Enable or disable MFA on an account.
|
1
docs/references/account/verify-authenticator.md
Normal file
1
docs/references/account/verify-authenticator.md
Normal file
|
@ -0,0 +1 @@
|
|||
Verify an authenticator app after adding it using the [add authenticator](/docs/references/cloud/client-web/account#addAuthenticator) method.
|
1
docs/references/databases/create-attribute-enum.md
Normal file
1
docs/references/databases/create-attribute-enum.md
Normal file
|
@ -0,0 +1 @@
|
|||
Create an enumeration attribute. The `elements` param acts as a white-list of accepted values for this attribute.
|
1
docs/references/databases/create-datetime-attribute.md
Normal file
1
docs/references/databases/create-datetime-attribute.md
Normal file
|
@ -0,0 +1 @@
|
|||
Create a date time attribute according to the ISO 8601 standard.
|
2
docs/references/databases/create-index.md
Normal file
2
docs/references/databases/create-index.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request.
|
||||
Attributes can be `key`, `fulltext`, and `unique`.
|
1
docs/references/databases/delete-attribute.md
Normal file
1
docs/references/databases/delete-attribute.md
Normal file
|
@ -0,0 +1 @@
|
|||
Deletes an attribute.
|
1
docs/references/databases/delete-index.md
Normal file
1
docs/references/databases/delete-index.md
Normal file
|
@ -0,0 +1 @@
|
|||
Delete an index.
|
1
docs/references/databases/get-attribute.md
Normal file
1
docs/references/databases/get-attribute.md
Normal file
|
@ -0,0 +1 @@
|
|||
Get attribute by ID.
|
1
docs/references/databases/get-index.md
Normal file
1
docs/references/databases/get-index.md
Normal file
|
@ -0,0 +1 @@
|
|||
Get index by ID.
|
1
docs/references/databases/list-attributes.md
Normal file
1
docs/references/databases/list-attributes.md
Normal file
|
@ -0,0 +1 @@
|
|||
List attributes in the collection.
|
1
docs/references/databases/list-indexes.md
Normal file
1
docs/references/databases/list-indexes.md
Normal file
|
@ -0,0 +1 @@
|
|||
List indexes in the collection.
|
1
docs/references/databases/update-boolean-attribute.md
Normal file
1
docs/references/databases/update-boolean-attribute.md
Normal file
|
@ -0,0 +1 @@
|
|||
Update a boolean attribute. Changing the `default` value will not update already existing documents.
|
1
docs/references/databases/update-datetime-attribute.md
Normal file
1
docs/references/databases/update-datetime-attribute.md
Normal file
|
@ -0,0 +1 @@
|
|||
Update a date time attribute. Changing the `default` value will not update already existing documents.
|
1
docs/references/health/get-queue-functions.md
Normal file
1
docs/references/health/get-queue-functions.md
Normal file
|
@ -0,0 +1 @@
|
|||
Get the number of function executions that are waiting to be processed in the Appwrite internal queue server.
|
1
docs/references/messaging/delete-message.md
Normal file
1
docs/references/messaging/delete-message.md
Normal file
|
@ -0,0 +1 @@
|
|||
Delete a message. If the message is not a draft or scheduled, but has been sent, this will not recall the message.
|
1
docs/references/users/create-target.md
Normal file
1
docs/references/users/create-target.md
Normal file
|
@ -0,0 +1 @@
|
|||
Create a messaging target.
|
1
docs/references/users/delete-mfa.md
Normal file
1
docs/references/users/delete-mfa.md
Normal file
|
@ -0,0 +1 @@
|
|||
Delete an authenticator app.
|
1
docs/references/users/delete-target.md
Normal file
1
docs/references/users/delete-target.md
Normal file
|
@ -0,0 +1 @@
|
|||
Delete a messaging target.
|
1
docs/references/users/get-user-target.md
Normal file
1
docs/references/users/get-user-target.md
Normal file
|
@ -0,0 +1 @@
|
|||
Get a user's push notification target by ID.
|
1
docs/references/users/list-factors.md
Normal file
1
docs/references/users/list-factors.md
Normal file
|
@ -0,0 +1 @@
|
|||
List the factors available on the account to be used as a MFA challange.
|
1
docs/references/users/list-user-targets.md
Normal file
1
docs/references/users/list-user-targets.md
Normal file
|
@ -0,0 +1 @@
|
|||
List the messaging targets that are associated with a user.
|
1
docs/references/users/update-target.md
Normal file
1
docs/references/users/update-target.md
Normal file
|
@ -0,0 +1 @@
|
|||
Update a messaging target.
|
1
docs/references/users/update-user-mfa.md
Normal file
1
docs/references/users/update-user-mfa.md
Normal file
|
@ -0,0 +1 @@
|
|||
Enable or disable MFA on a user account.
|
|
@ -12,9 +12,10 @@ class PersonalData extends Password
|
|||
protected ?string $email = null,
|
||||
protected ?string $name = null,
|
||||
protected ?string $phone = null,
|
||||
protected bool $strict = false
|
||||
protected bool $strict = false,
|
||||
protected bool $allowEmpty = false,
|
||||
) {
|
||||
parent::__construct();
|
||||
parent::__construct($allowEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -99,33 +99,26 @@ class Schema
|
|||
/** @var Route $route */
|
||||
|
||||
$namespace = $route->getLabel('sdk.namespace', '');
|
||||
$methods = $route->getLabel('sdk.method', '');
|
||||
$method = $route->getLabel('sdk.method', '');
|
||||
$name = $namespace . \ucfirst($method);
|
||||
|
||||
if (!\is_array($methods)) {
|
||||
$methods = [$methods];
|
||||
if (empty($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($methods as $method) {
|
||||
$name = $namespace . \ucfirst($method);
|
||||
|
||||
if (empty($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Mapper::route($utopia, $route, $complexity) as $field) {
|
||||
switch ($route->getMethod()) {
|
||||
case 'GET':
|
||||
$queries[$name] = $field;
|
||||
break;
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
case 'DELETE':
|
||||
$mutations[$name] = $field;
|
||||
break;
|
||||
default:
|
||||
throw new \Exception("Unsupported method: {$route->getMethod()}");
|
||||
}
|
||||
foreach (Mapper::route($utopia, $route, $complexity) as $field) {
|
||||
switch ($route->getMethod()) {
|
||||
case 'GET':
|
||||
$queries[$name] = $field;
|
||||
break;
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
case 'DELETE':
|
||||
$mutations[$name] = $field;
|
||||
break;
|
||||
default:
|
||||
throw new \Exception("Unsupported method: {$route->getMethod()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,30 +168,35 @@ class Specs extends Action
|
|||
|
||||
foreach ($appRoutes as $key => $method) {
|
||||
foreach ($method as $route) {
|
||||
$hide = $route->getLabel('sdk.hide', false);
|
||||
if ($hide === true || (\is_array($hide) && \in_array($platform, $hide))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var \Utopia\Route $route */
|
||||
$routeSecurity = $route->getLabel('sdk.auth', []);
|
||||
$sdkPlaforms = [];
|
||||
$sdkPlatforms = [];
|
||||
|
||||
foreach ($routeSecurity as $value) {
|
||||
switch ($value) {
|
||||
case APP_AUTH_TYPE_SESSION:
|
||||
$sdkPlaforms[] = APP_PLATFORM_CLIENT;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
break;
|
||||
case APP_AUTH_TYPE_KEY:
|
||||
$sdkPlaforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_JWT:
|
||||
$sdkPlaforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_ADMIN:
|
||||
$sdkPlaforms[] = APP_PLATFORM_CONSOLE;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CONSOLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($routeSecurity)) {
|
||||
$sdkPlaforms[] = APP_PLATFORM_CLIENT;
|
||||
$sdkPlaforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
if (!$route->getLabel('docs', true)) {
|
||||
|
@ -210,7 +215,7 @@ class Specs extends Action
|
|||
continue;
|
||||
}
|
||||
|
||||
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlaforms)) {
|
||||
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlatforms)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,13 +19,13 @@ use Utopia\Database\Database;
|
|||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Migration\Destinations\Appwrite as DestinationsAppwrite;
|
||||
use Utopia\Migration\Resource;
|
||||
use Utopia\Migration\Source;
|
||||
use Utopia\Migration\Sources\Appwrite;
|
||||
use Utopia\Migration\Sources\Firebase;
|
||||
use Utopia\Migration\Sources\NHost;
|
||||
use Utopia\Migration\Sources\Supabase;
|
||||
use Utopia\Migration\Transfer;
|
||||
use Utopia\Migration\Exception as MigrationException;
|
||||
|
||||
class Migrations extends Action
|
||||
{
|
||||
|
@ -285,15 +285,21 @@ class Migrations extends Action
|
|||
$this->updateMigrationDocument($migrationDocument, $projectDocument);
|
||||
});
|
||||
|
||||
$errors = $transfer->getReport(Resource::STATUS_ERROR);
|
||||
$sourceErrors = $source->getErrors();
|
||||
$destinationErrors = $destination->getErrors();
|
||||
|
||||
if (count($errors) > 0) {
|
||||
if (!empty($sourceErrors) || !empty($destinationErrors)) {
|
||||
$migrationDocument->setAttribute('status', 'failed');
|
||||
$migrationDocument->setAttribute('stage', 'finished');
|
||||
|
||||
$errorMessages = [];
|
||||
foreach ($errors as $error) {
|
||||
$errorMessages[] = "Failed to transfer resource '{$error['id']}:{$error['resource']}' with message '{$error['message']}'";
|
||||
foreach ($sourceErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while fetching '{$error->getResourceType()}:{$error->getResourceId()}' from source with message: '{$error->getMessage()}'";
|
||||
}
|
||||
foreach ($destinationErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while pushing '{$error->getResourceType()}:{$error->getResourceId()}' to destination with message: '{$error->getMessage()}'";
|
||||
}
|
||||
|
||||
$migrationDocument->setAttribute('errors', $errorMessages);
|
||||
|
@ -318,21 +324,32 @@ class Migrations extends Action
|
|||
}
|
||||
|
||||
if ($transfer) {
|
||||
$errors = $transfer->getReport(Resource::STATUS_ERROR);
|
||||
$sourceErrors = $source->getErrors();
|
||||
$destinationErrors = $destination->getErrors();
|
||||
|
||||
if (count($errors) > 0) {
|
||||
$migrationDocument->setAttribute('status', 'failed');
|
||||
$migrationDocument->setAttribute('stage', 'finished');
|
||||
$migrationDocument->setAttribute('errors', $errors);
|
||||
$errorMessages = [];
|
||||
foreach ($sourceErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while fetching '{$error->getResourceType()}:{$error->getResourceId()}' from source with message '{$error->getMessage()}'";
|
||||
}
|
||||
foreach ($destinationErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while pushing '{$error->getResourceType()}:{$error->getResourceId()}' to destination with message '{$error->getMessage()}'";
|
||||
}
|
||||
|
||||
$migrationDocument->setAttribute('errors', $errorMessages);
|
||||
}
|
||||
} finally {
|
||||
if ($migrationDocument) {
|
||||
$this->updateMigrationDocument($migrationDocument, $projectDocument);
|
||||
}
|
||||
if ($tempAPIKey) {
|
||||
$this->removeAPIKey($tempAPIKey);
|
||||
}
|
||||
if ($migrationDocument) {
|
||||
$this->updateMigrationDocument($migrationDocument, $projectDocument);
|
||||
|
||||
if ($migrationDocument->getAttribute('status', '') == 'failed') {
|
||||
throw new Exception(implode("\n", $migrationDocument->getAttribute('errors', [])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,18 +120,10 @@ class OpenAPI3 extends Format
|
|||
foreach ($this->routes as $route) {
|
||||
$url = \str_replace('/v1', '', $route->getPath());
|
||||
$scope = $route->getLabel('scope', '');
|
||||
$hide = $route->getLabel('sdk.hide', false);
|
||||
$consumes = [$route->getLabel('sdk.request.type', 'application/json')];
|
||||
|
||||
if ($hide) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$method = $route->getLabel('sdk.method', [\uniqid()]);
|
||||
if (\is_array($method)) {
|
||||
$method = $method[0];
|
||||
}
|
||||
|
||||
$method = $route->getLabel('sdk.method', \uniqid());
|
||||
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__ . '/../../../../' . $route->getLabel('sdk.description', '')) : null;
|
||||
$produces = $route->getLabel('sdk.response.type', null);
|
||||
$model = $route->getLabel('sdk.response.model', 'none');
|
||||
|
@ -156,12 +148,8 @@ class OpenAPI3 extends Format
|
|||
}
|
||||
|
||||
if (empty($routeSecurity)) {
|
||||
if (!$route->getLabel('sdk.hideServer', false)) {
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
}
|
||||
if (!$route->getLabel('sdk.hideClient', false)) {
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
$temp = [
|
||||
|
@ -175,6 +163,7 @@ class OpenAPI3 extends Format
|
|||
'weight' => $route->getOrder(),
|
||||
'cookies' => $route->getLabel('sdk.cookies', false),
|
||||
'type' => $route->getLabel('sdk.methodType', ''),
|
||||
'deprecated' => $route->getLabel('sdk.deprecated', false),
|
||||
'demo' => Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')) . '/' . Template::fromCamelCaseToDash($method) . '.md',
|
||||
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
|
||||
'rate-limit' => $route->getLabel('abuse-limit', 0),
|
||||
|
|
|
@ -118,18 +118,9 @@ class Swagger2 extends Format
|
|||
/** @var \Utopia\Route $route */
|
||||
$url = \str_replace('/v1', '', $route->getPath());
|
||||
$scope = $route->getLabel('scope', '');
|
||||
$hide = $route->getLabel('sdk.hide', false);
|
||||
$consumes = [$route->getLabel('sdk.request.type', 'application/json')];
|
||||
|
||||
if ($hide) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$method = $route->getLabel('sdk.method', [\uniqid()]);
|
||||
if (\is_array($method)) {
|
||||
$method = $method[0];
|
||||
}
|
||||
|
||||
$method = $route->getLabel('sdk.method', \uniqid());
|
||||
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__ . '/../../../../' . $route->getLabel('sdk.description', '')) : null;
|
||||
$produces = $route->getLabel('sdk.response.type', null);
|
||||
$model = $route->getLabel('sdk.response.model', 'none');
|
||||
|
@ -154,8 +145,8 @@ class Swagger2 extends Format
|
|||
}
|
||||
|
||||
if (empty($routeSecurity)) {
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
$temp = [
|
||||
|
@ -171,6 +162,7 @@ class Swagger2 extends Format
|
|||
'weight' => $route->getOrder(),
|
||||
'cookies' => $route->getLabel('sdk.cookies', false),
|
||||
'type' => $route->getLabel('sdk.methodType', ''),
|
||||
'deprecated' => $route->getLabel('sdk.deprecated', false),
|
||||
'demo' => Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')) . '/' . Template::fromCamelCaseToDash($method) . '.md',
|
||||
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
|
||||
'rate-limit' => $route->getLabel('abuse-limit', 0),
|
||||
|
|
|
@ -25,10 +25,7 @@ class Request extends UtopiaRequest
|
|||
$parameters = parent::getParams();
|
||||
|
||||
if (self::hasFilter() && self::hasRoute()) {
|
||||
$method = self::getRoute()->getLabel('sdk.method', ['unknown']);
|
||||
if (\is_array($method)) {
|
||||
$method = $method[0];
|
||||
}
|
||||
$method = self::getRoute()->getLabel('sdk.method', 'unknown');
|
||||
$endpointIdentifier = self::getRoute()->getLabel('sdk.namespace', 'unknown') . '.' . $method;
|
||||
$parameters = self::getFilter()->parse($parameters, $endpointIdentifier);
|
||||
}
|
||||
|
|
|
@ -2247,12 +2247,10 @@ class AccountCustomClientTest extends Scope
|
|||
$this->assertEmpty($response['body']['secret']);
|
||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
|
||||
|
||||
\sleep(10);
|
||||
|
||||
$smsRequest = $this->getLastRequest();
|
||||
|
||||
return \array_merge($data, [
|
||||
'token' => $smsRequest['data']['secret']
|
||||
'token' => \substr($smsRequest['data']['message'], 0, 6)
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -4532,6 +4532,8 @@ trait DatabasesBase
|
|||
$this->assertEquals(2, count($response['body']['documents']));
|
||||
$this->assertEquals(null, $response['body']['documents'][0]['fullName']);
|
||||
$this->assertArrayNotHasKey("libraries", $response['body']['documents'][0]);
|
||||
$this->assertArrayNotHasKey('$databaseId', $response['body']['documents'][0]);
|
||||
$this->assertArrayNotHasKey('$collectionId', $response['body']['documents'][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4551,6 +4553,8 @@ trait DatabasesBase
|
|||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertArrayNotHasKey('libraries', $response['body']['documents'][0]);
|
||||
$this->assertArrayNotHasKey('$databaseId', $response['body']['documents'][0]);
|
||||
$this->assertArrayNotHasKey('$collectionId', $response['body']['documents'][0]);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -4563,6 +4567,8 @@ trait DatabasesBase
|
|||
$document = $response['body']['documents'][0];
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertArrayHasKey('libraries', $document);
|
||||
$this->assertArrayNotHasKey('$databaseId', $document);
|
||||
$this->assertArrayNotHasKey('$collectionId', $document);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents/' . $document['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
|
|
@ -6,6 +6,7 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
|
@ -316,6 +317,164 @@ class DatabasesCustomClientTest extends Scope
|
|||
$this->assertEquals('restrict', $collection1RelationAttribute['onDelete']);
|
||||
}
|
||||
|
||||
public function testRelationshipSameTwoWayKey(): void
|
||||
{
|
||||
$database = $this->client->call(Client::METHOD_POST, '/databases', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Same two way key'
|
||||
]);
|
||||
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
$collection1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'c1',
|
||||
'documentSecurity' => false,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
]
|
||||
]);
|
||||
|
||||
$collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'c2',
|
||||
'documentSecurity' => false,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
]
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_ONE_TO_ONE,
|
||||
'twoWay' => false,
|
||||
'onDelete' => 'cascade',
|
||||
'key' => 'attr1',
|
||||
'twoWayKey' => 'same_key'
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals(202, $relation['headers']['status-code']);
|
||||
$this->assertEquals('same_key', $relation['body']['twoWayKey']);
|
||||
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_ONE_TO_MANY,
|
||||
'twoWay' => false,
|
||||
'onDelete' => 'cascade',
|
||||
'key' => 'attr2',
|
||||
'twoWayKey' => 'same_key'
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals(409, $relation['body']['code']);
|
||||
$this->assertEquals('Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.', $relation['body']['message']);
|
||||
|
||||
// twoWayKey is null TwoWayKey is default
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_ONE_TO_MANY,
|
||||
'twoWay' => false,
|
||||
'onDelete' => 'cascade',
|
||||
'key' => 'attr3',
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals(202, $relation['headers']['status-code']);
|
||||
$this->assertArrayHasKey('twoWayKey', $relation['body']);
|
||||
|
||||
// twoWayKey is null, TwoWayKey is default, second POST
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_ONE_TO_MANY,
|
||||
'twoWay' => false,
|
||||
'onDelete' => 'cascade',
|
||||
'key' => 'attr4',
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals('Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.', $relation['body']['message']);
|
||||
$this->assertEquals(409, $relation['body']['code']);
|
||||
|
||||
// RelationshipManyToMany
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_MANY_TO_MANY,
|
||||
'twoWay' => true,
|
||||
'onDelete' => 'setNull',
|
||||
'key' => 'songs',
|
||||
'twoWayKey' => 'playlist',
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals(202, $relation['headers']['status-code']);
|
||||
$this->assertArrayHasKey('twoWayKey', $relation['body']);
|
||||
|
||||
// Second RelationshipManyToMany on Same collections
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_MANY_TO_MANY,
|
||||
'twoWay' => true,
|
||||
'onDelete' => 'setNull',
|
||||
'key' => 'songs2',
|
||||
'twoWayKey' => 'playlist2',
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals(409, $relation['body']['code']);
|
||||
$this->assertEquals('Creating more than one "manyToMany" relationship on the same collection is currently not permitted.', $relation['body']['message']);
|
||||
}
|
||||
|
||||
public function testUpdateWithoutRelationPermission(): void
|
||||
{
|
||||
$userId = $this->getUser()['$id'];
|
||||
|
|
|
@ -1731,6 +1731,19 @@ class ProjectsConsoleClientTest extends Scope
|
|||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge($this->getHeaders(), [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $id,
|
||||
'x-appwrite-mode' => 'admin',
|
||||
]), [
|
||||
// Empty password
|
||||
'email' => uniqid() . 'user@localhost.test',
|
||||
'name' => 'User',
|
||||
'userId' => ID::unique(),
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
$email = uniqid() . 'user@localhost.test';
|
||||
$userId = ID::unique();
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge($this->getHeaders(), [
|
||||
|
|
|
@ -30,6 +30,7 @@ trait TeamsBaseClient
|
|||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertIsInt($response['body']['total']);
|
||||
$this->assertNotEmpty($response['body']['memberships'][0]['$id']);
|
||||
$this->assertFalse($response['body']['memberships'][0]['mfa']);
|
||||
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']);
|
||||
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']);
|
||||
$this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']);
|
||||
|
@ -155,6 +156,7 @@ trait TeamsBaseClient
|
|||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertNotEmpty($response['body']['$id']);
|
||||
$this->assertFalse($response['body']['mfa']);
|
||||
$this->assertNotEmpty($response['body']['userId']);
|
||||
$this->assertNotEmpty($response['body']['userName']);
|
||||
$this->assertNotEmpty($response['body']['userEmail']);
|
||||
|
|
|
@ -51,6 +51,7 @@ trait TeamsBaseServer
|
|||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertNotEmpty($response['body']['$id']);
|
||||
$this->assertNotEmpty($response['body']['userId']);
|
||||
$this->assertFalse($response['body']['mfa']);
|
||||
$this->assertNotEmpty($response['body']['userName']);
|
||||
$this->assertNotEmpty($response['body']['userEmail']);
|
||||
$this->assertNotEmpty($response['body']['teamId']);
|
||||
|
|
Loading…
Reference in a new issue