1
0
Fork 0
mirror of synced 2024-06-15 01:04:51 +12:00

Merge branch '0.16.x' of https://github.com/appwrite/appwrite into feat-migration-1.0.0

This commit is contained in:
Torsten Dittmann 2022-09-14 10:20:42 +02:00
commit d0c3f52ab6
12 changed files with 86 additions and 16 deletions

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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>

View file

@ -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

View file

@ -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();

View file

@ -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;}

View file

@ -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;}

View file

@ -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;

View file

@ -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()];
}

View file

@ -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);

View file

@ -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);