Merge branch '0.16.x' of https://github.com/appwrite/appwrite into feat-migration-1.0.0
This commit is contained in:
commit
d0c3f52ab6
|
@ -2148,6 +2148,17 @@ $collections = [
|
|||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('scheduleUpdatedAt'), // Used to fix duplicate executions bug. Can be removed once new queue library is used
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('schedulePrevious'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
|
|
|
@ -1313,7 +1313,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
|
|||
->inject('dbForProject')
|
||||
->inject('database')
|
||||
->inject('events')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
|
||||
|
||||
$attribute = createAttribute($databaseId, $collectionId, new Document([
|
||||
'key' => $key,
|
||||
|
|
|
@ -84,6 +84,7 @@ App::post('/v1/functions')
|
|||
'deployment' => '',
|
||||
'events' => $events,
|
||||
'schedule' => $schedule,
|
||||
'scheduleUpdatedAt' => DateTime::now(),
|
||||
'schedulePrevious' => null,
|
||||
'scheduleNext' => null,
|
||||
'timeout' => $timeout,
|
||||
|
@ -445,6 +446,7 @@ App::put('/v1/functions/:functionId')
|
|||
$cron = (!empty($function->getAttribute('deployment')) && !empty($schedule)) ? new CronExpression($schedule) : null;
|
||||
$next = (!empty($function->getAttribute('deployment')) && !empty($schedule)) ? DateTime::format($cron->getNextRunDate()) : null;
|
||||
|
||||
$scheduleUpdatedAt = $schedule !== $original ? DateTime::now() : $function->getAttribute('scheduleUpdatedAt');
|
||||
$enabled ??= $function->getAttribute('enabled', true);
|
||||
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
|
||||
|
@ -452,6 +454,7 @@ App::put('/v1/functions/:functionId')
|
|||
'name' => $name,
|
||||
'events' => $events,
|
||||
'schedule' => $schedule,
|
||||
'scheduleUpdatedAt' => $scheduleUpdatedAt,
|
||||
'scheduleNext' => $next,
|
||||
'timeout' => $timeout,
|
||||
'enabled' => $enabled,
|
||||
|
|
|
@ -315,7 +315,7 @@ $permissions = $this->getParam('permissions', null);
|
|||
<div class="link new-attribute-boolean"><i class="avatar icon-boolean"></i> New Boolean Attribute</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link new-attribute-datetime"><i class="avatar icon-string"></i> New DateTime Attribute</div>
|
||||
<div class="link new-attribute-datetime"><i class="avatar icon-datetime"></i> New DateTime Attribute</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link new-attribute-url"><i class="avatar icon-link"></i> New URL Attribute</div>
|
||||
|
@ -719,10 +719,10 @@ $permissions = $this->getParam('permissions', null);
|
|||
|
||||
<label for="xdefault">Default Value</label>
|
||||
<template x-if="!(array || required)">
|
||||
<input name="xdefault" type="datetime-local" class="margin-bottom-large">
|
||||
<input name="xdefault" type="datetime-local" step=".001" data-cast-to="string-datetime" class="margin-bottom-large">
|
||||
</template>
|
||||
<template x-if="(array || required)">
|
||||
<input name="xdefault" type="datetime-local" class="margin-bottom-large" disabled value="">
|
||||
<input name="xdefault" type="datetime-local" step=".001" data-cast-to="string-datetime" class="margin-bottom-large" disabled value="">
|
||||
</template>
|
||||
|
||||
<footer>
|
||||
|
|
|
@ -102,7 +102,7 @@ $permissions = $this->getParam('permissions', null);
|
|||
<?php endif; ?>
|
||||
|
||||
<fieldset name="data" data-cast-to="object" data-ls-attrs="x-init=doc = {{project-document}}" x-data="{doc: {}}">
|
||||
<ul data-ls-attrs="x-init=attributes = {{project-collection.attributes}}" x-data="{attributes: []}">
|
||||
<ul data-ls-attrs="x-init=attributes = {{project-collection.attributes}}.map(function(attr) { if(attr.type === 'datetime' && doc[attr.key]) { doc[attr.key] = isoToLocal(doc[attr.key]) } return attr; })" x-data="{attributes: [], isoToLocal(isoTime) { const date = new Date(isoTime); const localTime = date.toLocaleString('sv').slice(0, 16).replace(' ', 'T'); return localTime; } }">
|
||||
<template x-for="attr in attributes.filter(a => a.status === 'available')">
|
||||
<li>
|
||||
<label>
|
||||
|
@ -151,7 +151,7 @@ $permissions = $this->getParam('permissions', null);
|
|||
:name="attr.key"
|
||||
:required="attr.required"
|
||||
x-model="doc[attr.key]"
|
||||
data-cast-to="string" />
|
||||
data-cast-to="string-datetime" />
|
||||
</template>
|
||||
<template x-if="attr.type === 'string' && !attr.format">
|
||||
<textarea
|
||||
|
@ -261,7 +261,7 @@ $permissions = $this->getParam('permissions', null);
|
|||
:name="attr.key"
|
||||
:required="attr.required"
|
||||
x-model="doc[attr.key][index]"
|
||||
data-cast-to="string" />
|
||||
data-cast-to="string-datetime" />
|
||||
</template>
|
||||
<template x-if="attr.type === 'string' && !attr.format">
|
||||
<textarea
|
||||
|
|
|
@ -129,7 +129,7 @@ class FunctionsV1 extends Worker
|
|||
break;
|
||||
|
||||
case 'schedule':
|
||||
$scheduleOriginal = $execution->getAttribute('scheduleOriginal', '');
|
||||
$functionOriginal = $function;
|
||||
/*
|
||||
* 1. Get Original Task
|
||||
* 2. Check for updates
|
||||
|
@ -150,21 +150,25 @@ class FunctionsV1 extends Worker
|
|||
throw new Exception('Function not found (' . $function->getId() . ')');
|
||||
}
|
||||
|
||||
if ($scheduleOriginal && $scheduleOriginal !== $function->getAttribute('schedule')) { // Schedule has changed from previous run, ignore this run.
|
||||
if ($functionOriginal->getAttribute('schedule') !== $function->getAttribute('schedule')) { // Schedule has changed from previous run, ignore this run.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($functionOriginal->getAttribute('scheduleUpdatedAt') !== $function->getAttribute('scheduleUpdatedAt')) { // Double execution due to rapid cron changes, ignore this run.
|
||||
return;
|
||||
}
|
||||
|
||||
$cron = new CronExpression($function->getAttribute('schedule'));
|
||||
$next = DateTime::format($cron->getNextRunDate());
|
||||
|
||||
$function
|
||||
$function = $function
|
||||
->setAttribute('scheduleNext', $next)
|
||||
->setAttribute('schedulePrevious', DateTime::now());
|
||||
|
||||
$function = $database->updateDocument(
|
||||
'functions',
|
||||
$function->getId(),
|
||||
$function->setAttribute('scheduleNext', $next)
|
||||
$function
|
||||
);
|
||||
|
||||
$reschedule = new Func();
|
||||
|
|
1
public/dist/scripts/app-all.js
vendored
1
public/dist/scripts/app-all.js
vendored
|
@ -3937,6 +3937,7 @@ request.onerror=function(){reject(new Error("Network Error"));};request.send(par
|
|||
return new Intl.DateTimeFormat(navigator.languages,{hourCycle:'h24',...format}).format(new Date(datetime));}
|
||||
return{format:format,}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,from,to,){if(value&&Array.isArray(value)&&to!=='array'){value=value.map(element=>cast(element,from,to));return value;}
|
||||
switch(to){case'int':case'integer':value=parseInt(value);break;case'numeric':value=Number(value);break;case'float':value=parseFloat(value);break;case'string':value=value.toString();if(value.length===0){value=null;}
|
||||
break;case'string-datetime':if(value.length===0){value=null;}else{const date=new Date(value);value=date.toISOString();}
|
||||
break;case'json':value=(value)?JSON.parse(value):[];break;case'array':if(value&&value.constructor&&value.constructor===Array){break;}
|
||||
if(from==='csv'){if(value.length===0){value=[];}else{value=value.split(',');}}else{value=[value];}
|
||||
break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
|
||||
|
|
1
public/dist/scripts/app.js
vendored
1
public/dist/scripts/app.js
vendored
|
@ -525,6 +525,7 @@ request.onerror=function(){reject(new Error("Network Error"));};request.send(par
|
|||
return new Intl.DateTimeFormat(navigator.languages,{hourCycle:'h24',...format}).format(new Date(datetime));}
|
||||
return{format:format,}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,from,to,){if(value&&Array.isArray(value)&&to!=='array'){value=value.map(element=>cast(element,from,to));return value;}
|
||||
switch(to){case'int':case'integer':value=parseInt(value);break;case'numeric':value=Number(value);break;case'float':value=parseFloat(value);break;case'string':value=value.toString();if(value.length===0){value=null;}
|
||||
break;case'string-datetime':if(value.length===0){value=null;}else{const date=new Date(value);value=date.toISOString();}
|
||||
break;case'json':value=(value)?JSON.parse(value):[];break;case'array':if(value&&value.constructor&&value.constructor===Array){break;}
|
||||
if(from==='csv'){if(value.length===0){value=[];}else{value=value.split(',');}}else{value=[value];}
|
||||
break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
|
||||
|
|
|
@ -25,6 +25,14 @@
|
|||
value = null;
|
||||
}
|
||||
break;
|
||||
case 'string-datetime':
|
||||
if (value.length === 0) {
|
||||
value = null;
|
||||
} else {
|
||||
const date = new Date(value);
|
||||
value = date.toISOString();
|
||||
}
|
||||
break;
|
||||
case 'json':
|
||||
value = (value) ? JSON.parse(value) : [];
|
||||
break;
|
||||
|
|
|
@ -9,6 +9,7 @@ use Appwrite\Auth\Hash\Phpass;
|
|||
use Appwrite\Auth\Hash\Scrypt;
|
||||
use Appwrite\Auth\Hash\Scryptmodified;
|
||||
use Appwrite\Auth\Hash\Sha;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Role;
|
||||
|
@ -421,6 +422,17 @@ class Auth
|
|||
if ($user->getId()) {
|
||||
$roles[] = Role::user($user->getId())->toString();
|
||||
$roles[] = Role::users()->toString();
|
||||
|
||||
$emailVerified = $user->getAttribute('emailVerification', false);
|
||||
$phoneVerified = $user->getAttribute('phoneVerification', false);
|
||||
|
||||
if ($emailVerified || $phoneVerified) {
|
||||
$roles[] = Role::user($user->getId(), Database::DIMENSION_VERIFIED)->toString();
|
||||
$roles[] = Role::users(Database::DIMENSION_VERIFIED)->toString();
|
||||
} else {
|
||||
$roles[] = Role::user($user->getId(), Database::DIMENSION_UNVERIFIED)->toString();
|
||||
$roles[] = Role::users(Database::DIMENSION_UNVERIFIED)->toString();
|
||||
}
|
||||
} else {
|
||||
return [Role::guests()->toString()];
|
||||
}
|
||||
|
|
|
@ -351,6 +351,8 @@ class AuthTest extends TestCase
|
|||
{
|
||||
$user = new Document([
|
||||
'$id' => ID::custom('123'),
|
||||
'emailVerification' => true,
|
||||
'phoneVerification' => true,
|
||||
'memberships' => [
|
||||
[
|
||||
'$id' => ID::custom('456'),
|
||||
|
@ -374,9 +376,11 @@ class AuthTest extends TestCase
|
|||
|
||||
$roles = Auth::getRoles($user);
|
||||
|
||||
$this->assertCount(9, $roles);
|
||||
$this->assertCount(11, $roles);
|
||||
$this->assertContains(Role::users()->toString(), $roles);
|
||||
$this->assertContains(Role::user(ID::custom('123'))->toString(), $roles);
|
||||
$this->assertContains(Role::users(Database::DIMENSION_VERIFIED)->toString(), $roles);
|
||||
$this->assertContains(Role::user(ID::custom('123'), Database::DIMENSION_VERIFIED)->toString(), $roles);
|
||||
$this->assertContains(Role::team(ID::custom('abc'))->toString(), $roles);
|
||||
$this->assertContains(Role::team(ID::custom('abc'), 'administrator')->toString(), $roles);
|
||||
$this->assertContains(Role::team(ID::custom('abc'), 'moderator')->toString(), $roles);
|
||||
|
@ -384,6 +388,21 @@ class AuthTest extends TestCase
|
|||
$this->assertContains(Role::team(ID::custom('def'), 'guest')->toString(), $roles);
|
||||
$this->assertContains(Role::member(ID::custom('456'))->toString(), $roles);
|
||||
$this->assertContains(Role::member(ID::custom('abc'))->toString(), $roles);
|
||||
|
||||
// Disable all verification
|
||||
$user['emailVerification'] = false;
|
||||
$user['phoneVerification'] = false;
|
||||
|
||||
$roles = Auth::getRoles($user);
|
||||
$this->assertContains(Role::users(Database::DIMENSION_UNVERIFIED)->toString(), $roles);
|
||||
$this->assertContains(Role::user(ID::custom('123'), Database::DIMENSION_UNVERIFIED)->toString(), $roles);
|
||||
|
||||
// Enable single verification type
|
||||
$user['emailVerification'] = true;
|
||||
|
||||
$roles = Auth::getRoles($user);
|
||||
$this->assertContains(Role::users(Database::DIMENSION_VERIFIED)->toString(), $roles);
|
||||
$this->assertContains(Role::user(ID::custom('123'), Database::DIMENSION_VERIFIED)->toString(), $roles);
|
||||
}
|
||||
|
||||
public function testPrivilegedUserRoles(): void
|
||||
|
@ -391,6 +410,8 @@ class AuthTest extends TestCase
|
|||
Authorization::setRole(Auth::USER_ROLE_OWNER);
|
||||
$user = new Document([
|
||||
'$id' => ID::custom('123'),
|
||||
'emailVerification' => true,
|
||||
'phoneVerification' => true,
|
||||
'memberships' => [
|
||||
[
|
||||
'$id' => ID::custom('def'),
|
||||
|
@ -417,6 +438,8 @@ class AuthTest extends TestCase
|
|||
$this->assertCount(7, $roles);
|
||||
$this->assertNotContains(Role::users()->toString(), $roles);
|
||||
$this->assertNotContains(Role::user(ID::custom('123'))->toString(), $roles);
|
||||
$this->assertNotContains(Role::users(Database::DIMENSION_VERIFIED)->toString(), $roles);
|
||||
$this->assertNotContains(Role::user(ID::custom('123'), Database::DIMENSION_VERIFIED)->toString(), $roles);
|
||||
$this->assertContains(Role::team(ID::custom('abc'))->toString(), $roles);
|
||||
$this->assertContains(Role::team(ID::custom('abc'), 'administrator')->toString(), $roles);
|
||||
$this->assertContains(Role::team(ID::custom('abc'), 'moderator')->toString(), $roles);
|
||||
|
|
|
@ -121,13 +121,20 @@ class MessagingChannelsTest extends TestCase
|
|||
|
||||
/**
|
||||
* Check for correct amount of subscriptions:
|
||||
* - XXX users
|
||||
* - XXX users (2 roles per user)
|
||||
* - XXX teams
|
||||
* - XXX team roles (3 roles per team)
|
||||
* - XXX team roles (2 roles per team)
|
||||
* - XXX member roles (2 roles per team)
|
||||
* - 1 guests
|
||||
* - 1 users
|
||||
* - 1 users unverified
|
||||
*/
|
||||
$this->assertCount(($this->connectionsAuthenticated + (4 * $this->connectionsPerChannel) + 2), $this->realtime->subscriptions['1']);
|
||||
$userRoles = 2 * $this->connectionsAuthenticated;
|
||||
$userGroupRoles = 2;
|
||||
$teamRoles = 2 * $this->connectionsPerChannel;
|
||||
$memberRoles = 2 * $this->connectionsPerChannel;
|
||||
$guestRoles = 1;
|
||||
$this->assertCount(($userRoles + $userGroupRoles + $teamRoles + $memberRoles + $guestRoles), $this->realtime->subscriptions['1']);
|
||||
|
||||
/**
|
||||
* Check for connections
|
||||
|
@ -139,7 +146,7 @@ class MessagingChannelsTest extends TestCase
|
|||
$this->realtime->unsubscribe(-1);
|
||||
|
||||
$this->assertCount($this->connectionsTotal, $this->realtime->connections);
|
||||
$this->assertCount(($this->connectionsAuthenticated + (4 * $this->connectionsPerChannel) + 2), $this->realtime->subscriptions['1']);
|
||||
$this->assertCount(($userRoles + $userGroupRoles + $teamRoles + $memberRoles + $guestRoles), $this->realtime->subscriptions['1']);
|
||||
|
||||
for ($i = 0; $i < $this->connectionsCount; $i++) {
|
||||
$this->realtime->unsubscribe($i);
|
||||
|
|
Loading…
Reference in a new issue