1
0
Fork 0
mirror of synced 2024-07-04 06:00:53 +12:00

Merge pull request #6015 from appwrite/1.4.x

Merge 1.4.x into cl-1.4.x
This commit is contained in:
Jake Barnby 2023-08-18 13:09:25 -04:00 committed by GitHub
commit fb0a4bde28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 1136 additions and 367 deletions

View file

@ -2,20 +2,21 @@
return [
// Codes based on: https://github.com/matomo-org/device-detector/blob/master/Parser/Client/Browser.php
'aa' => __DIR__ . '/browsers/avant.png',
'an' => __DIR__ . '/browsers/android-webview-beta.png',
'ch' => __DIR__ . '/browsers/chrome.png',
'ci' => __DIR__ . '/browsers/chrome.png', //Chrome Mobile iOS
'cm' => __DIR__ . '/browsers/chrome.png', //Chrome Mobile
'cr' => __DIR__ . '/browsers/chromium.png',
'ff' => __DIR__ . '/browsers/firefox.png',
'sf' => __DIR__ . '/browsers/safari.png',
'mf' => __DIR__ . '/browsers/safari.png',
'ps' => __DIR__ . '/browsers/edge.png',
'oi' => __DIR__ . '/browsers/edge.png',
'om' => __DIR__ . '/browsers/opera-mini.png',
'op' => __DIR__ . '/browsers/opera.png',
'on' => __DIR__ . '/browsers/opera.png',
'aa' => ['name' => 'Avant Browser', 'path' => __DIR__ . '/browsers/avant.png'],
'an' => ['name' => 'Android WebView Beta', 'path' => __DIR__ . '/browsers/android-webview-beta.png'],
'ch' => ['name' => 'Google Chrome', 'path' => __DIR__ . '/browsers/chrome.png'],
'ci' => ['name' => 'Google Chrome (iOS)', 'path' => __DIR__ . '/browsers/chrome.png'],
'cm' => ['name' => 'Google Chrome (Mobile)', 'path' => __DIR__ . '/browsers/chrome.png'],
'cr' => ['name' => 'Chromium', 'path' => __DIR__ . '/browsers/chromium.png'],
'ff' => ['name' => 'Mozilla Firefox', 'path' => __DIR__ . '/browsers/firefox.png'],
'sf' => ['name' => 'Safari', 'path' => __DIR__ . '/browsers/safari.png'],
'mf' => ['name' => 'Mobile Safari', 'path' => __DIR__ . '/browsers/safari.png'],
'ps' => ['name' => 'Microsoft Edge', 'path' => __DIR__ . '/browsers/edge.png'],
'oi' => ['name' => 'Microsoft Edge (iOS)', 'path' => __DIR__ . '/browsers/edge.png'],
'om' => ['name' => 'Opera Mini', 'path' => __DIR__ . '/browsers/opera-mini.png'],
'op' => ['name' => 'Opera', 'path' => __DIR__ . '/browsers/opera.png'],
'on' => ['name' => 'Opera (Next)', 'path' => __DIR__ . '/browsers/opera.png'],
/*
'36' => '360 Phone Browser',

View file

@ -1,20 +1,20 @@
<?php
return [
'amex' => __DIR__ . '/credit-cards/amex.png',
'argencard' => __DIR__ . '/credit-cards/argencard.png',
'cabal' => __DIR__ . '/credit-cards/cabal.png',
'censosud' => __DIR__ . '/credit-cards/consosud.png',
'diners' => __DIR__ . '/credit-cards/diners.png',
'discover' => __DIR__ . '/credit-cards/discover.png',
'elo' => __DIR__ . '/credit-cards/elo.png',
'hipercard' => __DIR__ . '/credit-cards/hipercard.png',
'jcb' => __DIR__ . '/credit-cards/jcb.png',
'mastercard' => __DIR__ . '/credit-cards/mastercard.png',
'naranja' => __DIR__ . '/credit-cards/naranja.png',
'targeta-shopping' => __DIR__ . '/credit-cards/tarjeta-shopping.png',
'union-china-pay' => __DIR__ . '/credit-cards/union-china-pay.png',
'visa' => __DIR__ . '/credit-cards/visa.png',
'mir' => __DIR__ . '/credit-cards/mir.png',
'maestro' => __DIR__ . '/credit-cards/maestro.png',
];
'amex' => ['name' => 'American Express', 'path' => __DIR__ . '/credit-cards/amex.png'],
'argencard' => ['name' => 'Argencard', 'path' => __DIR__ . '/credit-cards/argencard.png'],
'cabal' => ['name' => 'Cabal', 'path' => __DIR__ . '/credit-cards/cabal.png'],
'censosud' => ['name' => 'Consosud', 'path' => __DIR__ . '/credit-cards/consosud.png'],
'diners' => ['name' => 'Diners Club', 'path' => __DIR__ . '/credit-cards/diners.png'],
'discover' => ['name' => 'Discover', 'path' => __DIR__ . '/credit-cards/discover.png'],
'elo' => ['name' => 'Elo', 'path' => __DIR__ . '/credit-cards/elo.png'],
'hipercard' => ['name' => 'Hipercard', 'path' => __DIR__ . '/credit-cards/hipercard.png'],
'jcb' => ['name' => 'JCB', 'path' => __DIR__ . '/credit-cards/jcb.png'],
'mastercard' => ['name' => 'Mastercard', 'path' => __DIR__ . '/credit-cards/mastercard.png'],
'naranja' => ['name' => 'Naranja', 'path' => __DIR__ . '/credit-cards/naranja.png'],
'targeta-shopping' => ['name' => 'Tarjeta Shopping', 'path' => __DIR__ . '/credit-cards/tarjeta-shopping.png'],
'union-china-pay' => ['name' => 'Union China Pay', 'path' => __DIR__ . '/credit-cards/union-china-pay.png'],
'visa' => ['name' => 'Visa', 'path' => __DIR__ . '/credit-cards/visa.png'],
'mir' => ['name' => 'MIR', 'path' => __DIR__ . '/credit-cards/mir.png'],
'maestro' => ['name' => 'Maestro', 'path' => __DIR__ . '/credit-cards/maestro.png']
];

View file

@ -1,198 +1,198 @@
<?php
return [
'af' => __DIR__ . '/flags/af.png',
'ao' => __DIR__ . '/flags/ao.png',
'al' => __DIR__ . '/flags/al.png',
'ad' => __DIR__ . '/flags/ad.png',
'ae' => __DIR__ . '/flags/ae.png',
'ar' => __DIR__ . '/flags/ar.png',
'am' => __DIR__ . '/flags/am.png',
'ag' => __DIR__ . '/flags/ag.png',
'au' => __DIR__ . '/flags/au.png',
'at' => __DIR__ . '/flags/at.png',
'az' => __DIR__ . '/flags/az.png',
'bi' => __DIR__ . '/flags/bi.png',
'be' => __DIR__ . '/flags/be.png',
'bj' => __DIR__ . '/flags/bj.png',
'bf' => __DIR__ . '/flags/bf.png',
'bd' => __DIR__ . '/flags/bd.png',
'bg' => __DIR__ . '/flags/bg.png',
'bh' => __DIR__ . '/flags/bh.png',
'bs' => __DIR__ . '/flags/bs.png',
'ba' => __DIR__ . '/flags/ba.png',
'by' => __DIR__ . '/flags/by.png',
'bz' => __DIR__ . '/flags/bz.png',
'bo' => __DIR__ . '/flags/bo.png',
'br' => __DIR__ . '/flags/br.png',
'bb' => __DIR__ . '/flags/bb.png',
'bn' => __DIR__ . '/flags/bn.png',
'bt' => __DIR__ . '/flags/bt.png',
'bw' => __DIR__ . '/flags/bw.png',
'cf' => __DIR__ . '/flags/cf.png',
'ca' => __DIR__ . '/flags/ca.png',
'ch' => __DIR__ . '/flags/ch.png',
'cl' => __DIR__ . '/flags/cl.png',
'cn' => __DIR__ . '/flags/cn.png',
'ci' => __DIR__ . '/flags/ci.png',
'cm' => __DIR__ . '/flags/cm.png',
'cd' => __DIR__ . '/flags/cd.png',
'cg' => __DIR__ . '/flags/cg.png',
'co' => __DIR__ . '/flags/co.png',
'km' => __DIR__ . '/flags/km.png',
'cv' => __DIR__ . '/flags/cv.png',
'cr' => __DIR__ . '/flags/cr.png',
'cu' => __DIR__ . '/flags/cu.png',
'cy' => __DIR__ . '/flags/cy.png',
'cz' => __DIR__ . '/flags/cz.png',
'de' => __DIR__ . '/flags/de.png',
'dj' => __DIR__ . '/flags/dj.png',
'dm' => __DIR__ . '/flags/dm.png',
'dk' => __DIR__ . '/flags/dk.png',
'do' => __DIR__ . '/flags/do.png',
'dz' => __DIR__ . '/flags/dz.png',
'ec' => __DIR__ . '/flags/ec.png',
'eg' => __DIR__ . '/flags/eg.png',
'er' => __DIR__ . '/flags/er.png',
'es' => __DIR__ . '/flags/es.png',
'ee' => __DIR__ . '/flags/ee.png',
'et' => __DIR__ . '/flags/et.png',
'fi' => __DIR__ . '/flags/fi.png',
'fj' => __DIR__ . '/flags/fj.png',
'fr' => __DIR__ . '/flags/fr.png',
'fm' => __DIR__ . '/flags/fm.png',
'ga' => __DIR__ . '/flags/ga.png',
'gb' => __DIR__ . '/flags/gb.png',
'ge' => __DIR__ . '/flags/ge.png',
'gh' => __DIR__ . '/flags/gh.png',
'gn' => __DIR__ . '/flags/gn.png',
'gm' => __DIR__ . '/flags/gm.png',
'gw' => __DIR__ . '/flags/gw.png',
'gq' => __DIR__ . '/flags/gq.png',
'gr' => __DIR__ . '/flags/gr.png',
'gd' => __DIR__ . '/flags/gd.png',
'gt' => __DIR__ . '/flags/gt.png',
'gy' => __DIR__ . '/flags/gy.png',
'hn' => __DIR__ . '/flags/hn.png',
'hr' => __DIR__ . '/flags/hr.png',
'ht' => __DIR__ . '/flags/ht.png',
'hu' => __DIR__ . '/flags/hu.png',
'id' => __DIR__ . '/flags/id.png',
'in' => __DIR__ . '/flags/in.png',
'ie' => __DIR__ . '/flags/ie.png',
'ir' => __DIR__ . '/flags/ir.png',
'iq' => __DIR__ . '/flags/iq.png',
'is' => __DIR__ . '/flags/is.png',
'il' => __DIR__ . '/flags/il.png',
'it' => __DIR__ . '/flags/it.png',
'jm' => __DIR__ . '/flags/jm.png',
'jo' => __DIR__ . '/flags/jo.png',
'jp' => __DIR__ . '/flags/jp.png',
'kz' => __DIR__ . '/flags/kz.png',
'ke' => __DIR__ . '/flags/ke.png',
'kg' => __DIR__ . '/flags/kg.png',
'kh' => __DIR__ . '/flags/kh.png',
'ki' => __DIR__ . '/flags/ki.png',
'kn' => __DIR__ . '/flags/kn.png',
'kr' => __DIR__ . '/flags/kr.png',
'kw' => __DIR__ . '/flags/kw.png',
'la' => __DIR__ . '/flags/la.png',
'lb' => __DIR__ . '/flags/lb.png',
'lr' => __DIR__ . '/flags/lr.png',
'ly' => __DIR__ . '/flags/ly.png',
'lc' => __DIR__ . '/flags/lc.png',
'li' => __DIR__ . '/flags/li.png',
'lk' => __DIR__ . '/flags/lk.png',
'ls' => __DIR__ . '/flags/ls.png',
'lt' => __DIR__ . '/flags/ls.png',
'lu' => __DIR__ . '/flags/lu.png',
'lv' => __DIR__ . '/flags/lv.png',
'ma' => __DIR__ . '/flags/ma.png',
'mc' => __DIR__ . '/flags/mc.png',
'md' => __DIR__ . '/flags/md.png',
'mg' => __DIR__ . '/flags/mg.png',
'mv' => __DIR__ . '/flags/mv.png',
'mx' => __DIR__ . '/flags/mx.png',
'mh' => __DIR__ . '/flags/mh.png',
'mk' => __DIR__ . '/flags/mk.png',
'ml' => __DIR__ . '/flags/ml.png',
'mt' => __DIR__ . '/flags/mt.png',
'mm' => __DIR__ . '/flags/mm.png',
'me' => __DIR__ . '/flags/me.png',
'mn' => __DIR__ . '/flags/mn.png',
'mz' => __DIR__ . '/flags/mz.png',
'mr' => __DIR__ . '/flags/mr.png',
'mu' => __DIR__ . '/flags/mu.png',
'mw' => __DIR__ . '/flags/mw.png',
'my' => __DIR__ . '/flags/my.png',
'na' => __DIR__ . '/flags/na.png',
'ne' => __DIR__ . '/flags/ne.png',
'ng' => __DIR__ . '/flags/ng.png',
'ni' => __DIR__ . '/flags/ni.png',
'nl' => __DIR__ . '/flags/nl.png',
'no' => __DIR__ . '/flags/no.png',
'np' => __DIR__ . '/flags/np.png',
'nr' => __DIR__ . '/flags/nr.png',
'nz' => __DIR__ . '/flags/nz.png',
'om' => __DIR__ . '/flags/om.png',
'pk' => __DIR__ . '/flags/pk.png',
'pa' => __DIR__ . '/flags/pa.png',
'pe' => __DIR__ . '/flags/pe.png',
'ph' => __DIR__ . '/flags/ph.png',
'pw' => __DIR__ . '/flags/pw.png',
'pg' => __DIR__ . '/flags/pg.png',
'pl' => __DIR__ . '/flags/pl.png',
'kp' => __DIR__ . '/flags/kp.png',
'pt' => __DIR__ . '/flags/pt.png',
'py' => __DIR__ . '/flags/py.png',
'qa' => __DIR__ . '/flags/qa.png',
'ro' => __DIR__ . '/flags/ro.png',
'ru' => __DIR__ . '/flags/ru.png',
'rw' => __DIR__ . '/flags/rw.png',
'sa' => __DIR__ . '/flags/sa.png',
'sd' => __DIR__ . '/flags/sd.png',
'sn' => __DIR__ . '/flags/sn.png',
'sg' => __DIR__ . '/flags/sg.png',
'sb' => __DIR__ . '/flags/sb.png',
'sl' => __DIR__ . '/flags/sl.png',
'sv' => __DIR__ . '/flags/sv.png',
'sm' => __DIR__ . '/flags/sm.png',
'so' => __DIR__ . '/flags/so.png',
'rs' => __DIR__ . '/flags/rs.png',
'ss' => __DIR__ . '/flags/ss.png',
'st' => __DIR__ . '/flags/st.png',
'sr' => __DIR__ . '/flags/sr.png',
'sk' => __DIR__ . '/flags/sk.png',
'si' => __DIR__ . '/flags/si.png',
'se' => __DIR__ . '/flags/se.png',
'sz' => __DIR__ . '/flags/sz.png',
'sc' => __DIR__ . '/flags/sc.png',
'sy' => __DIR__ . '/flags/sy.png',
'td' => __DIR__ . '/flags/td.png',
'tg' => __DIR__ . '/flags/tg.png',
'th' => __DIR__ . '/flags/th.png',
'tj' => __DIR__ . '/flags/tj.png',
'tm' => __DIR__ . '/flags/tm.png',
'tl' => __DIR__ . '/flags/tl.png',
'to' => __DIR__ . '/flags/to.png',
'tt' => __DIR__ . '/flags/tt.png',
'tn' => __DIR__ . '/flags/tn.png',
'tr' => __DIR__ . '/flags/tr.png',
'tv' => __DIR__ . '/flags/tv.png',
'tz' => __DIR__ . '/flags/tz.png',
'ug' => __DIR__ . '/flags/ug.png',
'ua' => __DIR__ . '/flags/ua.png',
'uy' => __DIR__ . '/flags/uy.png',
'us' => __DIR__ . '/flags/us.png',
'uz' => __DIR__ . '/flags/uz.png',
'va' => __DIR__ . '/flags/va.png',
'vc' => __DIR__ . '/flags/vc.png',
've' => __DIR__ . '/flags/ve.png',
'vn' => __DIR__ . '/flags/vn.png',
'vu' => __DIR__ . '/flags/vu.png',
'ws' => __DIR__ . '/flags/ws.png',
'ye' => __DIR__ . '/flags/ye.png',
'za' => __DIR__ . '/flags/za.png',
'zm' => __DIR__ . '/flags/zm.png',
'zw' => __DIR__ . '/flags/zw.png',
'af' => ['name' => 'Afghanistan', 'path' => __DIR__ . '/flags/af.png'],
'ao' => ['name' => 'Angola', 'path' => __DIR__ . '/flags/ao.png'],
'al' => ['name' => 'Albania', 'path' => __DIR__ . '/flags/al.png'],
'ad' => ['name' => 'Andorra', 'path' => __DIR__ . '/flags/ad.png'],
'ae' => ['name' => 'United Arab Emirates', 'path' => __DIR__ . '/flags/ae.png'],
'ar' => ['name' => 'Argentina', 'path' => __DIR__ . '/flags/ar.png'],
'am' => ['name' => 'Armenia', 'path' => __DIR__ . '/flags/am.png'],
'ag' => ['name' => 'Antigua and Barbuda', 'path' => __DIR__ . '/flags/ag.png'],
'au' => ['name' => 'Australia', 'path' => __DIR__ . '/flags/au.png'],
'at' => ['name' => 'Austria', 'path' => __DIR__ . '/flags/at.png'],
'az' => ['name' => 'Azerbaijan', 'path' => __DIR__ . '/flags/az.png'],
'bi' => ['name' => 'Burundi', 'path' => __DIR__ . '/flags/bi.png'],
'be' => ['name' => 'Belgium', 'path' => __DIR__ . '/flags/be.png'],
'bj' => ['name' => 'Benin', 'path' => __DIR__ . '/flags/bj.png'],
'bf' => ['name' => 'Burkina Faso', 'path' => __DIR__ . '/flags/bf.png'],
'bd' => ['name' => 'Bangladesh', 'path' => __DIR__ . '/flags/bd.png'],
'bg' => ['name' => 'Bulgaria', 'path' => __DIR__ . '/flags/bg.png'],
'bh' => ['name' => 'Bahrain', 'path' => __DIR__ . '/flags/bh.png'],
'bs' => ['name' => 'Bahamas', 'path' => __DIR__ . '/flags/bs.png'],
'ba' => ['name' => 'Bosnia and Herzegovina', 'path' => __DIR__ . '/flags/ba.png'],
'by' => ['name' => 'Belarus', 'path' => __DIR__ . '/flags/by.png'],
'bz' => ['name' => 'Belize', 'path' => __DIR__ . '/flags/bz.png'],
'bo' => ['name' => 'Bolivia', 'path' => __DIR__ . '/flags/bo.png'],
'br' => ['name' => 'Brazil', 'path' => __DIR__ . '/flags/br.png'],
'bb' => ['name' => 'Barbados', 'path' => __DIR__ . '/flags/bb.png'],
'bn' => ['name' => 'Brunei Darussalam', 'path' => __DIR__ . '/flags/bn.png'],
'bt' => ['name' => 'Bhutan', 'path' => __DIR__ . '/flags/bt.png'],
'bw' => ['name' => 'Botswana', 'path' => __DIR__ . '/flags/bw.png'],
'cf' => ['name' => 'Central African Republic', 'path' => __DIR__ . '/flags/cf.png'],
'ca' => ['name' => 'Canada', 'path' => __DIR__ . '/flags/ca.png'],
'ch' => ['name' => 'Switzerland', 'path' => __DIR__ . '/flags/ch.png'],
'cl' => ['name' => 'Chile', 'path' => __DIR__ . '/flags/cl.png'],
'cn' => ['name' => 'China', 'path' => __DIR__ . '/flags/cn.png'],
'ci' => ['name' => 'Côte d\'Ivoire', 'path' => __DIR__ . '/flags/ci.png'],
'cm' => ['name' => 'Cameroon', 'path' => __DIR__ . '/flags/cm.png'],
'cd' => ['name' => 'Democratic Republic of the Congo', 'path' => __DIR__ . '/flags/cd.png'],
'cg' => ['name' => 'Republic of the Congo', 'path' => __DIR__ . '/flags/cg.png'],
'co' => ['name' => 'Colombia', 'path' => __DIR__ . '/flags/co.png'],
'km' => ['name' => 'Comoros', 'path' => __DIR__ . '/flags/km.png'],
'cv' => ['name' => 'Cape Verde', 'path' => __DIR__ . '/flags/cv.png'],
'cr' => ['name' => 'Costa Rica', 'path' => __DIR__ . '/flags/cr.png'],
'cu' => ['name' => 'Cuba', 'path' => __DIR__ . '/flags/cu.png'],
'cy' => ['name' => 'Cyprus', 'path' => __DIR__ . '/flags/cy.png'],
'cz' => ['name' => 'Czech Republic', 'path' => __DIR__ . '/flags/cz.png'],
'de' => ['name' => 'Germany', 'path' => __DIR__ . '/flags/de.png'],
'dj' => ['name' => 'Djibouti', 'path' => __DIR__ . '/flags/dj.png'],
'dm' => ['name' => 'Dominica', 'path' => __DIR__ . '/flags/dm.png'],
'dk' => ['name' => 'Denmark', 'path' => __DIR__ . '/flags/dk.png'],
'do' => ['name' => 'Dominican Republic', 'path' => __DIR__ . '/flags/do.png'],
'dz' => ['name' => 'Algeria', 'path' => __DIR__ . '/flags/dz.png'],
'ec' => ['name' => 'Ecuador', 'path' => __DIR__ . '/flags/ec.png'],
'eg' => ['name' => 'Egypt', 'path' => __DIR__ . '/flags/eg.png'],
'er' => ['name' => 'Eritrea', 'path' => __DIR__ . '/flags/er.png'],
'es' => ['name' => 'Spain', 'path' => __DIR__ . '/flags/es.png'],
'ee' => ['name' => 'Estonia', 'path' => __DIR__ . '/flags/ee.png'],
'et' => ['name' => 'Ethiopia', 'path' => __DIR__ . '/flags/et.png'],
'fi' => ['name' => 'Finland', 'path' => __DIR__ . '/flags/fi.png'],
'fj' => ['name' => 'Fiji', 'path' => __DIR__ . '/flags/fj.png'],
'fr' => ['name' => 'France', 'path' => __DIR__ . '/flags/fr.png'],
'fm' => ['name' => 'Micronesia (Federated States of)', 'path' => __DIR__ . '/flags/fm.png'],
'ga' => ['name' => 'Gabon', 'path' => __DIR__ . '/flags/ga.png'],
'gb' => ['name' => 'United Kingdom', 'path' => __DIR__ . '/flags/gb.png'],
'ge' => ['name' => 'Georgia', 'path' => __DIR__ . '/flags/ge.png'],
'gh' => ['name' => 'Ghana', 'path' => __DIR__ . '/flags/gh.png'],
'gn' => ['name' => 'Guinea', 'path' => __DIR__ . '/flags/gn.png'],
'gm' => ['name' => 'Gambia', 'path' => __DIR__ . '/flags/gm.png'],
'gw' => ['name' => 'Guinea-Bissau', 'path' => __DIR__ . '/flags/gw.png'],
'gq' => ['name' => 'Equatorial Guinea', 'path' => __DIR__ . '/flags/gq.png'],
'gr' => ['name' => 'Greece', 'path' => __DIR__ . '/flags/gr.png'],
'gd' => ['name' => 'Grenada', 'path' => __DIR__ . '/flags/gd.png'],
'gt' => ['name' => 'Guatemala', 'path' => __DIR__ . '/flags/gt.png'],
'gy' => ['name' => 'Guyana', 'path' => __DIR__ . '/flags/gy.png'],
'hn' => ['name' => 'Honduras', 'path' => __DIR__ . '/flags/hn.png'],
'hr' => ['name' => 'Croatia', 'path' => __DIR__ . '/flags/hr.png'],
'ht' => ['name' => 'Haiti', 'path' => __DIR__ . '/flags/ht.png'],
'hu' => ['name' => 'Hungary', 'path' => __DIR__ . '/flags/hu.png'],
'id' => ['name' => 'Indonesia', 'path' => __DIR__ . '/flags/id.png'],
'in' => ['name' => 'India', 'path' => __DIR__ . '/flags/in.png'],
'ie' => ['name' => 'Ireland', 'path' => __DIR__ . '/flags/ie.png'],
'ir' => ['name' => 'Iran (Islamic Republic of)', 'path' => __DIR__ . '/flags/ir.png'],
'iq' => ['name' => 'Iraq', 'path' => __DIR__ . '/flags/iq.png'],
'is' => ['name' => 'Iceland', 'path' => __DIR__ . '/flags/is.png'],
'il' => ['name' => 'Israel', 'path' => __DIR__ . '/flags/il.png'],
'it' => ['name' => 'Italy', 'path' => __DIR__ . '/flags/it.png'],
'jm' => ['name' => 'Jamaica', 'path' => __DIR__ . '/flags/jm.png'],
'jo' => ['name' => 'Jordan', 'path' => __DIR__ . '/flags/jo.png'],
'jp' => ['name' => 'Japan', 'path' => __DIR__ . '/flags/jp.png'],
'kz' => ['name' => 'Kazakhstan', 'path' => __DIR__ . '/flags/kz.png'],
'ke' => ['name' => 'Kenya', 'path' => __DIR__ . '/flags/ke.png'],
'kg' => ['name' => 'Kyrgyzstan', 'path' => __DIR__ . '/flags/kg.png'],
'kh' => ['name' => 'Cambodia', 'path' => __DIR__ . '/flags/kh.png'],
'ki' => ['name' => 'Kiribati', 'path' => __DIR__ . '/flags/ki.png'],
'kn' => ['name' => 'Saint Kitts and Nevis', 'path' => __DIR__ . '/flags/kn.png'],
'kr' => ['name' => 'South Korea', 'path' => __DIR__ . '/flags/kr.png'],
'kw' => ['name' => 'Kuwait', 'path' => __DIR__ . '/flags/kw.png'],
'la' => ['name' => 'Lao People\'s Democratic Republic', 'path' => __DIR__ . '/flags/la.png'],
'lb' => ['name' => 'Lebanon', 'path' => __DIR__ . '/flags/lb.png'],
'lr' => ['name' => 'Liberia', 'path' => __DIR__ . '/flags/lr.png'],
'ly' => ['name' => 'Libya', 'path' => __DIR__ . '/flags/ly.png'],
'lc' => ['name' => 'Saint Lucia', 'path' => __DIR__ . '/flags/lc.png'],
'li' => ['name' => 'Liechtenstein', 'path' => __DIR__ . '/flags/li.png'],
'lk' => ['name' => 'Sri Lanka', 'path' => __DIR__ . '/flags/lk.png'],
'ls' => ['name' => 'Lesotho', 'path' => __DIR__ . '/flags/ls.png'],
'lt' => ['name' => 'Lithuania', 'path' => __DIR__ . '/flags/lt.png'],
'lu' => ['name' => 'Luxembourg', 'path' => __DIR__ . '/flags/lu.png'],
'lv' => ['name' => 'Latvia', 'path' => __DIR__ . '/flags/lv.png'],
'ma' => ['name' => 'Morocco', 'path' => __DIR__ . '/flags/ma.png'],
'mc' => ['name' => 'Monaco', 'path' => __DIR__ . '/flags/mc.png'],
'md' => ['name' => 'Moldova', 'path' => __DIR__ . '/flags/md.png'],
'mg' => ['name' => 'Madagascar', 'path' => __DIR__ . '/flags/mg.png'],
'mv' => ['name' => 'Maldives', 'path' => __DIR__ . '/flags/mv.png'],
'mx' => ['name' => 'Mexico', 'path' => __DIR__ . '/flags/mx.png'],
'mh' => ['name' => 'Marshall Islands', 'path' => __DIR__ . '/flags/mh.png'],
'mk' => ['name' => 'North Macedonia', 'path' => __DIR__ . '/flags/mk.png'],
'ml' => ['name' => 'Mali', 'path' => __DIR__ . '/flags/ml.png'],
'mt' => ['name' => 'Malta', 'path' => __DIR__ . '/flags/mt.png'],
'mm' => ['name' => 'Myanmar', 'path' => __DIR__ . '/flags/mm.png'],
'me' => ['name' => 'Montenegro', 'path' => __DIR__ . '/flags/me.png'],
'mn' => ['name' => 'Mongolia', 'path' => __DIR__ . '/flags/mn.png'],
'mz' => ['name' => 'Mozambique', 'path' => __DIR__ . '/flags/mz.png'],
'mr' => ['name' => 'Mauritania', 'path' => __DIR__ . '/flags/mr.png'],
'mu' => ['name' => 'Mauritius', 'path' => __DIR__ . '/flags/mu.png'],
'mw' => ['name' => 'Malawi', 'path' => __DIR__ . '/flags/mw.png'],
'my' => ['name' => 'Malaysia', 'path' => __DIR__ . '/flags/my.png'],
'na' => ['name' => 'Namibia', 'path' => __DIR__ . '/flags/na.png'],
'ne' => ['name' => 'Niger', 'path' => __DIR__ . '/flags/ne.png'],
'ng' => ['name' => 'Nigeria', 'path' => __DIR__ . '/flags/ng.png'],
'ni' => ['name' => 'Nicaragua', 'path' => __DIR__ . '/flags/ni.png'],
'nl' => ['name' => 'Netherlands', 'path' => __DIR__ . '/flags/nl.png'],
'no' => ['name' => 'Norway', 'path' => __DIR__ . '/flags/no.png'],
'np' => ['name' => 'Nepal', 'path' => __DIR__ . '/flags/np.png'],
'nr' => ['name' => 'Nauru', 'path' => __DIR__ . '/flags/nr.png'],
'nz' => ['name' => 'New Zealand', 'path' => __DIR__ . '/flags/nz.png'],
'om' => ['name' => 'Oman', 'path' => __DIR__ . '/flags/om.png'],
'pk' => ['name' => 'Pakistan', 'path' => __DIR__ . '/flags/pk.png'],
'pa' => ['name' => 'Panama', 'path' => __DIR__ . '/flags/pa.png'],
'pe' => ['name' => 'Peru', 'path' => __DIR__ . '/flags/pe.png'],
'ph' => ['name' => 'Philippines', 'path' => __DIR__ . '/flags/ph.png'],
'pw' => ['name' => 'Palau', 'path' => __DIR__ . '/flags/pw.png'],
'pg' => ['name' => 'Papua New Guinea', 'path' => __DIR__ . '/flags/pg.png'],
'pl' => ['name' => 'Poland', 'path' => __DIR__ . '/flags/pl.png'],
'kp' => ['name' => 'North Korea', 'path' => __DIR__ . '/flags/kp.png'],
'pt' => ['name' => 'Portugal', 'path' => __DIR__ . '/flags/pt.png'],
'py' => ['name' => 'Paraguay', 'path' => __DIR__ . '/flags/py.png'],
'qa' => ['name' => 'Qatar', 'path' => __DIR__ . '/flags/qa.png'],
'ro' => ['name' => 'Romania', 'path' => __DIR__ . '/flags/ro.png'],
'ru' => ['name' => 'Russia', 'path' => __DIR__ . '/flags/ru.png'],
'rw' => ['name' => 'Rwanda', 'path' => __DIR__ . '/flags/rw.png'],
'sa' => ['name' => 'Saudi Arabia', 'path' => __DIR__ . '/flags/sa.png'],
'sd' => ['name' => 'Sudan', 'path' => __DIR__ . '/flags/sd.png'],
'sn' => ['name' => 'Senegal', 'path' => __DIR__ . '/flags/sn.png'],
'sg' => ['name' => 'Singapore', 'path' => __DIR__ . '/flags/sg.png'],
'sb' => ['name' => 'Solomon Islands', 'path' => __DIR__ . '/flags/sb.png'],
'sl' => ['name' => 'Sierra Leone', 'path' => __DIR__ . '/flags/sl.png'],
'sv' => ['name' => 'El Salvador', 'path' => __DIR__ . '/flags/sv.png'],
'sm' => ['name' => 'San Marino', 'path' => __DIR__ . '/flags/sm.png'],
'so' => ['name' => 'Somalia', 'path' => __DIR__ . '/flags/so.png'],
'rs' => ['name' => 'Serbia', 'path' => __DIR__ . '/flags/rs.png'],
'ss' => ['name' => 'South Sudan', 'path' => __DIR__ . '/flags/ss.png'],
'st' => ['name' => 'Sao Tome and Principe', 'path' => __DIR__ . '/flags/st.png'],
'sr' => ['name' => 'Suriname', 'path' => __DIR__ . '/flags/sr.png'],
'sk' => ['name' => 'Slovakia', 'path' => __DIR__ . '/flags/sk.png'],
'si' => ['name' => 'Slovenia', 'path' => __DIR__ . '/flags/si.png'],
'se' => ['name' => 'Sweden', 'path' => __DIR__ . '/flags/se.png'],
'sz' => ['name' => 'Eswatini', 'path' => __DIR__ . '/flags/sz.png'],
'sc' => ['name' => 'Seychelles', 'path' => __DIR__ . '/flags/sc.png'],
'sy' => ['name' => 'Syria', 'path' => __DIR__ . '/flags/sy.png'],
'td' => ['name' => 'Chad', 'path' => __DIR__ . '/flags/td.png'],
'tg' => ['name' => 'Togo', 'path' => __DIR__ . '/flags/tg.png'],
'th' => ['name' => 'Thailand', 'path' => __DIR__ . '/flags/th.png'],
'tj' => ['name' => 'Tajikistan', 'path' => __DIR__ . '/flags/tj.png'],
'tm' => ['name' => 'Turkmenistan', 'path' => __DIR__ . '/flags/tm.png'],
'tl' => ['name' => 'Timor-Leste', 'path' => __DIR__ . '/flags/tl.png'],
'to' => ['name' => 'Tonga', 'path' => __DIR__ . '/flags/to.png'],
'tt' => ['name' => 'Trinidad and Tobago', 'path' => __DIR__ . '/flags/tt.png'],
'tn' => ['name' => 'Tunisia', 'path' => __DIR__ . '/flags/tn.png'],
'tr' => ['name' => 'Turkey', 'path' => __DIR__ . '/flags/tr.png'],
'tv' => ['name' => 'Tuvalu', 'path' => __DIR__ . '/flags/tv.png'],
'tz' => ['name' => 'Tanzania', 'path' => __DIR__ . '/flags/tz.png'],
'ug' => ['name' => 'Uganda', 'path' => __DIR__ . '/flags/ug.png'],
'ua' => ['name' => 'Ukraine', 'path' => __DIR__ . '/flags/ua.png'],
'uy' => ['name' => 'Uruguay', 'path' => __DIR__ . '/flags/uy.png'],
'us' => ['name' => 'United States', 'path' => __DIR__ . '/flags/us.png'],
'uz' => ['name' => 'Uzbekistan', 'path' => __DIR__ . '/flags/uz.png'],
'va' => ['name' => 'Vatican City', 'path' => __DIR__ . '/flags/va.png'],
'vc' => ['name' => 'Saint Vincent and the Grenadines', 'path' => __DIR__ . '/flags/vc.png'],
've' => ['name' => 'Venezuela', 'path' => __DIR__ . '/flags/ve.png'],
'vn' => ['name' => 'Vietnam', 'path' => __DIR__ . '/flags/vn.png'],
'vu' => ['name' => 'Vanuatu', 'path' => __DIR__ . '/flags/vu.png'],
'ws' => ['name' => 'Samoa', 'path' => __DIR__ . '/flags/ws.png'],
'ye' => ['name' => 'Yemen', 'path' => __DIR__ . '/flags/ye.png'],
'za' => ['name' => 'South Africa', 'path' => __DIR__ . '/flags/za.png'],
'zm' => ['name' => 'Zambia', 'path' => __DIR__ . '/flags/zm.png'],
'zw' => ['name' => 'Zimbabwe', 'path' => __DIR__ . '/flags/zw.png'],
];

View file

@ -362,10 +362,6 @@ return [
"code" => "ko",
"name" => "Korean",
],
[
"code" => "ko",
"name" => "Korean (Johab)",
],
[
"code" => "ku",
"name" => "Kurdish",

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

View file

@ -940,7 +940,7 @@ App::delete('/v1/account/identities/:identityId')
->label('sdk.description', '/docs/references/account/delete-identity.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('identityId', [], new UID(), 'Identity ID.')
->param('identityId', '', new UID(), 'Identity ID.')
->inject('response')
->inject('dbForProject')
->action(function (string $identityId, Response $response, Database $dbForProject) {

View file

@ -42,7 +42,7 @@ $avatarCallback = function (string $type, string $code, int $width, int $height,
}
$output = 'png';
$path = $set[$code];
$path = $set[$code]['path'];
$type = 'png';
if (!\is_readable($path)) {

View file

@ -8,8 +8,10 @@ use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Attributes;
use Appwrite\Utopia\Database\Validator\Queries\Collections;
use Appwrite\Utopia\Database\Validator\Queries\Databases;
use Appwrite\Utopia\Database\Validator\Queries\Indexes;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
use Utopia\App;
@ -194,16 +196,14 @@ function createAttribute(string $databaseId, string $collectionId, Document $att
->setType(DATABASE_TYPE_CREATE_ATTRIBUTE)
->setDatabase($db)
->setCollection($collection)
->setDocument($attribute)
;
->setDocument($attribute);
$events
->setContext('collection', $collection)
->setContext('database', $db)
->setParam('databaseId', $databaseId)
->setParam('collectionId', $collection->getId())
->setParam('attributeId', $attribute->getId())
;
->setParam('attributeId', $attribute->getId());
$response->setStatusCode(Response::STATUS_CODE_CREATED);
@ -680,13 +680,11 @@ App::delete('/v1/databases/:databaseId')
$deletes
->setType(DELETE_TYPE_DOCUMENT)
->setDocument($database)
;
->setDocument($database);
$events
->setParam('databaseId', $database->getId())
->setPayload($response->output($database, Response::MODEL_DATABASE))
;
->setPayload($response->output($database, Response::MODEL_DATABASE));
$response->noContent();
});
@ -1628,9 +1626,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_LIST)
->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('queries', [], new Attributes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true)
->inject('response')
->inject('dbForProject')
->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject) {
->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject) {
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
@ -1644,11 +1643,33 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$attributes = $collection->getAttribute('attributes');
$queries = Query::parseQueries($queries);
\array_push($queries, Query::equal('collectionId', [$collectionId]), Query::equal('databaseId', [$databaseId]));
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = reset($cursor);
if ($cursor) {
$attributeId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn() => $dbForProject->find('attributes', [
Query::equal('collectionId', [$collectionId]),
Query::equal('databaseId', [$databaseId]),
Query::equal('key', [$attributeId]),
Query::limit(1),
]));
if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Attribute '{$attributeId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument[0]);
}
$filterQueries = Query::groupByType($queries)['filters'];
$response->dynamic(new Document([
'total' => \count($attributes),
'attributes' => $attributes
'total' => $dbForProject->count('attributes', $filterQueries, APP_LIMIT_COUNT),
'attributes' => $dbForProject->find('attributes', $queries),
]), Response::MODEL_ATTRIBUTE_LIST);
});
@ -2432,26 +2453,50 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
->label('sdk.response.model', Response::MODEL_INDEX_LIST)
->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('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true)
->inject('response')
->inject('dbForProject')
->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject) {
->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject) {
$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);
if ($collection->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$indexes = $collection->getAttribute('indexes');
$queries = Query::parseQueries($queries);
\array_push($queries, Query::equal('collectionId', [$collectionId]), Query::equal('databaseId', [$databaseId]));
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = reset($cursor);
if ($cursor) {
$indexId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn() => $dbForProject->find('indexes', [
Query::equal('collectionId', [$collectionId]),
Query::equal('databaseId', [$databaseId]),
Query::equal('key', [$indexId]),
Query::limit(1)
]));
if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Index '{$indexId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument[0]);
}
$filterQueries = Query::groupByType($queries)['filters'];
$response->dynamic(new Document([
'total' => \count($indexes),
'indexes' => $indexes,
'total' => $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT),
'indexes' => $dbForProject->find('indexes', $queries),
]), Response::MODEL_INDEX_LIST);
});
@ -3212,28 +3257,13 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
}
$data = \array_merge($document->getArrayCopy(), $data); // Merge existing data with new data
$data['$collection'] = $collection->getId(); // Make sure user doesn't switch collectionID
$data['$collection'] = $document->getAttribute('$collection'); // Make sure user doesn't switch collectionID
$data['$createdAt'] = $document->getCreatedAt(); // Make sure user doesn't switch createdAt
$data['$id'] = $document->getId(); // Make sure user doesn't switch document unique ID
$data['$permissions'] = $permissions;
$newDocument = new Document($data);
$checkPermissions = function (Document $collection, Document $document, Document $old, string $permission) use (&$checkPermissions, $dbForProject, $database) {
$documentSecurity = $collection->getAttribute('documentSecurity', false);
$validator = new Authorization($permission);
$valid = $validator->isValid($collection->getPermissionsByType($permission));
if (!$documentSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($permission === Database::PERMISSION_UPDATE) {
$valid = $valid || $validator->isValid($old->getPermissionsByType($permission));
if ($documentSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
}
$setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database) {
$relationships = \array_filter(
$collection->getAttribute('attributes', []),
fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
@ -3260,6 +3290,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
);
foreach ($relations as &$relation) {
// If the relation is an array it can be either update or create a child document.
if (
\is_array($relation)
&& \array_values($relation) !== $relation
@ -3273,21 +3304,20 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(),
$relation->getId()
));
$relation->removeAttribute('$collectionId');
$relation->removeAttribute('$databaseId');
// Attribute $collection is required for Utopia.
$relation->setAttribute(
'$collection',
'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId()
);
if ($oldDocument->isEmpty()) {
$type = Database::PERMISSION_CREATE;
if (isset($relation['$id']) && $relation['$id'] === 'unique()') {
$relation['$id'] = ID::unique();
}
} else {
$relation->removeAttribute('$collectionId');
$relation->removeAttribute('$databaseId');
$relation->setAttribute('$collection', $relatedCollection->getId());
$type = Database::PERMISSION_UPDATE;
}
$checkPermissions($relatedCollection, $relation, $oldDocument, $type);
$setCollection($relatedCollection, $relation);
}
}
@ -3297,26 +3327,26 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
$document->setAttribute($relationship->getAttribute('key'), \reset($relations));
}
}
};
});
$checkPermissions($collection, $newDocument, $document, Database::PERMISSION_UPDATE);
$setCollection($collection, $newDocument);
try {
$document = $dbForProject->withRequestTimestamp(
$requestTimestamp,
fn() => $dbForProject->updateDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$document->getId(),
$newDocument
)
);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (DuplicateException) {
throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS);
} catch (StructureException $exception) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage());
}
try {
$document = $dbForProject->withRequestTimestamp(
$requestTimestamp,
fn() => $dbForProject->updateDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$document->getId(),
$newDocument
)
);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (DuplicateException) {
throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS);
} catch (StructureException $exception) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage());
}
// Add $collectionId and $databaseId for all documents
$processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) {

View file

@ -739,7 +739,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
});
App::patch('/v1/teams/:teamId/memberships/:membershipId')
->desc('Update Membership Roles')
->desc('Update Membership')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].memberships.[membershipId].update')
->label('scope', 'teams.write')
@ -747,8 +747,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
->label('sdk.method', 'updateMembershipRoles')
->label('sdk.description', '/docs/references/teams/update-team-membership-roles.md')
->label('sdk.method', 'updateMembership')
->label('sdk.description', '/docs/references/teams/update-team-membership.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)

View file

@ -256,10 +256,11 @@ App::init()
$deletes->setProject($project);
$database->setProject($project);
$calculateUsage = fn ($event, Document $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject);
$dbForProject
->on(Database::EVENT_DOCUMENT_CREATE, fn ($event, $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject))
->on(Database::EVENT_DOCUMENT_DELETE, fn ($event, $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject))
;
->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', $calculateUsage)
->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', $calculateUsage);
$useCache = $route->getLabel('cache', false);

View file

@ -947,7 +947,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
if (APP_MODE_ADMIN !== $mode) {
if ($project->isEmpty()) {
$user = new Document(['$id' => ID::custom(''), '$collection' => 'users']);
$user = new Document([]);
} else {
$user = $dbForProject->getDocument('users', Auth::$unique);
}
@ -959,14 +959,14 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
$user->isEmpty() // Check a document has been found in the DB
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret, $authDuration)
) { // Validate user has valid login token
$user = new Document(['$id' => ID::custom(''), '$collection' => 'users']);
$user = new Document([]);
}
if (APP_MODE_ADMIN === $mode) {
if ($user->find('teamId', $project->getAttribute('teamId'), 'memberships')) {
Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
} else {
$user = new Document(['$id' => ID::custom(''), '$collection' => 'users']);
$user = new Document([]);
}
}
@ -989,7 +989,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
}
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
$user = new Document(['$id' => ID::custom(''), '$collection' => 'users']);
$user = new Document([]);
}
}

View file

@ -60,7 +60,7 @@ class MigrationsV1 extends Worker
return;
}
$this->dbForProject = $this->getProjectDB(new Document($this->args['project']));
$this->dbForProject = $this->getProjectDB($project);
$this->processMigration();
}

View file

@ -43,13 +43,13 @@
"ext-sockets": "*",
"appwrite/php-clamav": "2.0.*",
"appwrite/php-runtimes": "0.11.*",
"utopia-php/abuse": "0.27.*",
"utopia-php/abuse": "0.31.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.29.*",
"utopia-php/audit": "0.33.*",
"utopia-php/cache": "0.8.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.38.*",
"utopia-php/database": "0.42.*",
"utopia-php/domains": "1.1.*",
"utopia-php/dsn": "0.1.*",
"utopia-php/framework": "0.28.*",

66
composer.lock generated
View file

@ -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": "ca47a56f2285cea787de1f58a8ee968c",
"content-hash": "2098172fc4b71eb0d41dcdbfea2f5061",
"packages": [
{
"name": "adhocore/jwt",
@ -1266,23 +1266,23 @@
},
{
"name": "utopia-php/abuse",
"version": "0.27.0",
"version": "0.31.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "d1115f5843e903ffaba9c23e450b33c0fe265ae0"
"reference": "d771c2c8d7d1237b1d04b5bf57b07a9b4736e627"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/d1115f5843e903ffaba9c23e450b33c0fe265ae0",
"reference": "d1115f5843e903ffaba9c23e450b33c0fe265ae0",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/d771c2c8d7d1237b1d04b5bf57b07a9b4736e627",
"reference": "d771c2c8d7d1237b1d04b5bf57b07a9b4736e627",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-pdo": "*",
"php": ">=8.0",
"utopia-php/database": "0.38.*"
"utopia-php/database": "0.42.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@ -1309,9 +1309,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/0.27.0"
"source": "https://github.com/utopia-php/abuse/tree/0.31.0"
},
"time": "2023-07-15T00:53:50+00:00"
"time": "2023-08-11T01:17:15+00:00"
},
{
"name": "utopia-php/analytics",
@ -1361,21 +1361,21 @@
},
{
"name": "utopia-php/audit",
"version": "0.29.0",
"version": "0.33.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "5318538f457bf73623629345c98ea06371ca5dd4"
"reference": "6fb82331c58c66cbdb8a419314a687fcb18a1d22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/5318538f457bf73623629345c98ea06371ca5dd4",
"reference": "5318538f457bf73623629345c98ea06371ca5dd4",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/6fb82331c58c66cbdb8a419314a687fcb18a1d22",
"reference": "6fb82331c58c66cbdb8a419314a687fcb18a1d22",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "0.38.*"
"utopia-php/database": "0.42.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@ -1402,9 +1402,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/0.29.0"
"source": "https://github.com/utopia-php/audit/tree/0.33.0"
},
"time": "2023-07-15T00:51:10+00:00"
"time": "2023-08-11T01:17:28+00:00"
},
{
"name": "utopia-php/cache",
@ -1557,16 +1557,16 @@
},
{
"name": "utopia-php/database",
"version": "0.38.0",
"version": "0.42.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "59e4684cf87e03c12dab9240158c1dfc6888e534"
"reference": "9ff69a9b9eadc581771798833d423829c9d8cc90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/59e4684cf87e03c12dab9240158c1dfc6888e534",
"reference": "59e4684cf87e03c12dab9240158c1dfc6888e534",
"url": "https://api.github.com/repos/utopia-php/database/zipball/9ff69a9b9eadc581771798833d423829c9d8cc90",
"reference": "9ff69a9b9eadc581771798833d423829c9d8cc90",
"shasum": ""
},
"require": {
@ -1607,9 +1607,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.38.0"
"source": "https://github.com/utopia-php/database/tree/0.42.1"
},
"time": "2023-07-14T07:49:38+00:00"
"time": "2023-08-14T16:09:09+00:00"
},
{
"name": "utopia-php/domains",
@ -3092,16 +3092,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.16.0",
"version": "v4.17.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "19526a33fb561ef417e822e85f08a00db4059c17"
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17",
"reference": "19526a33fb561ef417e822e85f08a00db4059c17",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"shasum": ""
},
"require": {
@ -3142,9 +3142,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
},
"time": "2023-06-25T14:52:30+00:00"
"time": "2023-08-13T19:53:39+00:00"
},
{
"name": "phar-io/manifest",
@ -3369,16 +3369,16 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.7.2",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d"
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"shasum": ""
},
"require": {
@ -3421,9 +3421,9 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3"
},
"time": "2023-05-30T18:13:47+00:00"
"time": "2023-08-12T11:01:26+00:00"
},
{
"name": "phpspec/prophecy",

View file

@ -84,7 +84,7 @@ services:
- ./docs:/usr/src/code/docs
- ./public:/usr/src/code/public
- ./src:/usr/src/code/src
- ./dev:/usr/local/dev
- ./dev:/usr/src/code/dev
depends_on:
- mariadb
- redis

View file

@ -1 +1 @@
Modify the roles of a team member. Only team members with the owner role have access to this endpoint. Learn more about [roles and permissions](/docs/permissions).
Modify the roles of a team member. Only team members with the owner role have access to this endpoint. Learn more about [roles and permissions](/docs/permissions).

View file

@ -252,6 +252,8 @@ class Mapper
case 'Appwrite\Utopia\Database\Validator\Queries\Base':
case 'Appwrite\Utopia\Database\Validator\Queries\Buckets':
case 'Appwrite\Utopia\Database\Validator\Queries\Collections':
case 'Appwrite\Utopia\Database\Validator\Queries\Attributes':
case 'Appwrite\Utopia\Database\Validator\Queries\Indexes':
case 'Appwrite\Utopia\Database\Validator\Queries\Databases':
case 'Appwrite\Utopia\Database\Validator\Queries\Deployments':
case 'Utopia\Database\Validator\Queries\Documents':

View file

@ -3,6 +3,7 @@
namespace Appwrite\Specification;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Route;
use Appwrite\Utopia\Response\Model;
@ -38,6 +39,17 @@ abstract class Format
'license.url' => '',
];
/*
* Blacklist to omit the enum types for the given route's parameter
*/
protected array $enumBlacklist = [
[
'namespace' => 'users',
'method' => 'getUsage',
'parameter' => 'provider'
]
];
public function __construct(App $app, array $services, array $routes, array $models, array $keys, int $authCount)
{
$this->app = $app;
@ -97,4 +109,141 @@ abstract class Format
{
return $this->params[$key] ?? $default;
}
protected function getEnumName(string $service, string $method, string $param): ?string
{
switch ($service) {
case 'account':
switch ($method) {
case 'createOAuth2Session':
return 'Provider';
}
break;
case 'avatars':
switch ($method) {
case 'getBrowser':
return 'Browser';
case 'getCreditCard':
return 'CreditCard';
case 'getFlag':
return 'Flag';
}
break;
case 'storage':
switch ($method) {
case 'getFilePreview':
switch ($param) {
case 'gravity':
return 'ImageGravity';
case 'output':
return 'ImageFormat';
}
break;
}
break;
case 'databases':
switch ($method) {
case 'createRelationshipAttribute':
switch ($param) {
case 'type':
return 'RelationshipType';
case 'onDelete':
return 'RelationMutate';
}
break;
case 'updateRelationshipAttribute':
switch ($param) {
case 'onDelete':
return 'RelationMutate';
}
break;
case 'createIndex':
switch ($param) {
case 'type':
return 'IndexType';
case 'orders':
return 'OrderBy';
}
}
break;
case 'projects':
switch ($method) {
case 'createPlatform':
switch ($param) {
case 'type':
return 'PlatformType';
}
break;
}
break;
}
return null;
}
public function getEnumKeys(string $service, string $method, string $param): array
{
$values = [];
switch ($service) {
case 'avatars':
switch ($method) {
case 'getBrowser':
$codes = Config::getParam('avatar-browsers');
foreach ($codes as $code => $value) {
$values[] = $value['name'];
}
return $values;
case 'getCreditCard':
$codes = Config::getParam('avatar-credit-cards');
foreach ($codes as $code => $value) {
$values[] = $value['name'];
}
return $values;
case 'getFlag':
$codes = Config::getParam('avatar-flags');
foreach ($codes as $code => $value) {
$values[] = $value['name'];
}
return $values;
}
break;
case 'databases':
switch ($method) {
case 'getUsage':
case 'getCollectionUsage':
case 'getDatabaseUsage':
// Range Enum Keys
$values = ['Twenty Four Hours', 'Seven Days', 'Thirty Days', 'Ninety Days'];
return $values;
}
break;
case 'function':
switch ($method) {
case 'getUsage':
case 'getFunctionUsage':
// Range Enum Keys
$values = ['Twenty Four Hours', 'Seven Days', 'Thirty Days', 'Ninety Days'];
return $values;
}
break;
case 'users':
switch ($method) {
case 'getUsage':
case 'getUserUsage':
// Range Enum Keys
if ($param == 'range') {
$values = ['Twenty Four Hours', 'Seven Days', 'Thirty Days', 'Ninety Days'];
return $values;
}
}
break;
case 'storage':
switch ($method) {
case 'getUsage':
case 'getBucketUsage':
// Range Enum Keys
$values = ['Twenty Four Hours', 'Seven Days', 'Thirty Days', 'Ninety Days'];
return $values;
}
}
return $values;
}
}

View file

@ -343,6 +343,8 @@ class OpenAPI3 extends Format
case 'Utopia\Validator\ArrayList':
case 'Appwrite\Utopia\Database\Validator\Queries\Buckets':
case 'Appwrite\Utopia\Database\Validator\Queries\Collections':
case 'Appwrite\Utopia\Database\Validator\Queries\Indexes':
case 'Appwrite\Utopia\Database\Validator\Queries\Attributes':
case 'Appwrite\Utopia\Database\Validator\Queries\Databases':
case 'Appwrite\Utopia\Database\Validator\Queries\Deployments':
case 'Utopia\Database\Validator\Queries\Documents':
@ -412,6 +414,25 @@ class OpenAPI3 extends Format
$node['schema']['type'] = $validator->getType();
$node['schema']['x-example'] = $validator->getList()[0];
//Iterate from the blackList. If it matches with the current one, then it is a blackList
// Do not add the enum
$allowed = true;
foreach ($this->enumBlacklist as $blacklist) {
if (
$blacklist['namespace'] == $route->getLabel('sdk.namespace', '')
&& $blacklist['method'] == $route->getLabel('sdk.method', '')
&& $blacklist['parameter'] == $name
) {
$allowed = false;
break;
}
}
if ($allowed) {
$node['schema']['enum'] = $validator->getList();
$node['schema']['x-enum-name'] = $this->getEnumName($route->getLabel('sdk.namespace', ''), $route->getLabel('sdk.method', ''), $name);
$node['schema']['x-enum-keys'] = $this->getEnumKeys($route->getLabel('sdk.namespace', ''), $route->getLabel('sdk.method', ''), $name);
}
if ($validator->getType() === 'integer') {
$node['format'] = 'int32';
}
@ -442,6 +463,13 @@ class OpenAPI3 extends Format
'x-example' => $node['schema']['x-example'] ?? null
];
if (isset($node['schema']['enum'])) {
/// If the enum flag is Set, add the enum values to the body
$body['content'][$consumes[0]]['schema']['properties'][$name]['enum'] = $node['schema']['enum'];
$body['content'][$consumes[0]]['schema']['properties'][$name]['x-enum-name'] = $node['schema']['x-enum-name'] ?? null;
$body['content'][$consumes[0]]['schema']['properties'][$name]['x-enum-keys'] = $node['schema']['x-enum-keys'] ?? null;
}
if ($node['schema']['x-upload-id'] ?? false) {
$body['content'][$consumes[0]]['schema']['properties'][$name]['x-upload-id'] = $node['schema']['x-upload-id'];
}

View file

@ -293,6 +293,7 @@ class Swagger2 extends Format
$validator = $validator->getValidator();
}
switch ((!empty($validator)) ? \get_class($validator) : '') {
case 'Utopia\Validator\Text':
$node['type'] = $validator->getType();
@ -342,6 +343,8 @@ class Swagger2 extends Format
case 'Utopia\Validator\ArrayList':
case 'Appwrite\Utopia\Database\Validator\Queries\Buckets':
case 'Appwrite\Utopia\Database\Validator\Queries\Collections':
case 'Appwrite\Utopia\Database\Validator\Queries\Indexes':
case 'Appwrite\Utopia\Database\Validator\Queries\Attributes':
case 'Appwrite\Utopia\Database\Validator\Queries\Databases':
case 'Appwrite\Utopia\Database\Validator\Queries\Deployments':
case 'Utopia\Database\Validator\Queries\Documents':
@ -414,6 +417,22 @@ class Swagger2 extends Format
$node['type'] = $validator->getType();
$node['x-example'] = $validator->getList()[0];
//Iterate from the blackList. If it matches with the current one, then it is a blackList
// Do not add the enum
$allowed = true;
foreach ($this->enumBlacklist as $blacklist) {
if ($blacklist['namespace'] == $route->getLabel('sdk.namespace', '') && $blacklist['method'] == $route->getLabel('sdk.method', '') && $blacklist['parameter'] == $name) {
$allowed = false;
break;
}
}
if ($allowed) {
$node['enum'] = $validator->getList();
$node['x-enum-name'] = $this->getEnumName($route->getLabel('sdk.namespace', ''), $route->getLabel('sdk.method', ''), $name);
$node['x-enum-keys'] = $this->getEnumKeys($route->getLabel('sdk.namespace', ''), $route->getLabel('sdk.method', ''), $name);
}
if ($validator->getType() === 'integer') {
$node['format'] = 'int32';
}
@ -452,6 +471,13 @@ class Swagger2 extends Format
'x-example' => $node['x-example'] ?? null,
];
if (isset($node['enum'])) {
/// If the enum flag is Set, add the enum values to the body
$body['schema']['properties'][$name]['enum'] = $node['enum'];
$body['schema']['properties'][$name]['x-enum-name'] = $node['x-enum-name'] ?? null;
$body['schema']['properties'][$name]['x-enum-keys'] = $node['x-enum-keys'] ?? null;
}
if ($node['x-global'] ?? false) {
$body['schema']['properties'][$name]['x-global'] = true;
}

View file

@ -0,0 +1,27 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Select;
class Attributes extends Base
{
public const ALLOWED_ATTRIBUTES = [
'key',
'type',
'size',
'required',
'array',
'status',
'error'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('attributes', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -2,13 +2,13 @@
namespace Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Extend\Exception;
use Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\Query\Filter;
use Utopia\Database\Validator\Query\Order;
use Utopia\Database\Validator\Query\Select;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
@ -69,7 +69,6 @@ class Base extends Queries
new Cursor(),
new Filter($attributes),
new Order($attributes),
new Select($attributes),
];
parent::__construct($validators);

View file

@ -0,0 +1,23 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Indexes extends Base
{
public const ALLOWED_ATTRIBUTES = [
'key',
'type',
'status',
'attributes',
'error',
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('indexes', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -3,6 +3,7 @@
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Utopia\Database\Document;
class AttributeRelationship extends Attribute
{
@ -73,4 +74,23 @@ class AttributeRelationship extends Attribute
{
return Response::MODEL_ATTRIBUTE_RELATIONSHIP;
}
/**
* Process Document before returning it to the client
*
* @return Document
*/
public function filter(Document $document): Document
{
$options = $document->getAttribute('options');
if (!\is_null($options)) {
$document->setAttribute('relatedCollection', $options['relatedCollection']);
$document->setAttribute('relationType', $options['relationType']);
$document->setAttribute('twoWay', $options['twoWay']);
$document->setAttribute('twoWayKey', $options['twoWayKey']);
$document->setAttribute('side', $options['side']);
$document->setAttribute('onDelete', $options['onDelete']);
}
return $document;
}
}

View file

@ -30,7 +30,7 @@ class Migration extends Model
])
->addRule('status', [
'type' => self::TYPE_STRING,
'description' => 'Migration status ( pending, processing, failed. completed ) ',
'description' => 'Migration status ( pending, processing, failed, completed ) ',
'default' => '',
'example' => 'pending',
])

View file

@ -5,10 +5,12 @@ namespace Tests\E2E\Services\Databases;
use Appwrite\Extend\Exception;
use Tests\E2E\Client;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\DateTime;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
trait DatabasesBase
@ -316,6 +318,32 @@ trait DatabasesBase
return $data;
}
/**
* @depends testCreateAttributes
*/
public function testListAttributes(array $data): void
{
$databaseId = $data['databaseId'];
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => ['equal("type", "string")', 'limit(2)', 'cursorAfter(title)'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, \count($response['body']['attributes']));
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => ['select(["key"])'],
]);
$this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']);
$this->assertEquals(400, $response['headers']['status-code']);
}
/**
* @depends testCreateAttributes
*/
@ -698,7 +726,6 @@ trait DatabasesBase
$this->assertEquals(10, $attributes['body']['total']);
$attributes = $attributes['body']['attributes'];
$this->assertIsArray($attributes);
$this->assertCount(10, $attributes);
@ -1048,6 +1075,32 @@ trait DatabasesBase
return $data;
}
/**
* @depends testCreateIndexes
*/
public function testListIndexes(array $data): void
{
$databaseId = $data['databaseId'];
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => ['equal("type", "key")', 'limit(2)'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, \count($response['body']['indexes']));
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => ['select(["key"])'],
]);
$this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']);
$this->assertEquals(400, $response['headers']['status-code']);
}
/**
* @depends testCreateIndexes
*/
@ -1325,7 +1378,7 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'select(["title", "releaseYear"])',
'select(["title", "releaseYear", "$id"])',
],
]);
@ -4045,10 +4098,9 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'select(["libraries.*"])',
'select(["libraries.*", "$id"])',
],
]);
$document = $response['body']['documents'][0];
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayHasKey('libraries', $document);
@ -4058,7 +4110,7 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'select(["fullName"])'
'select(["fullName", "$id"])'
],
]);

View file

@ -316,4 +316,420 @@ class DatabasesCustomClientTest extends Scope
$this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']);
$this->assertEquals('restrict', $collection1RelationAttribute['onDelete']);
}
public function testUpdateWithoutRelationPermission(): void
{
$userId = $this->getUser()['$id'];
$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' => ID::unique(),
]);
$databaseId = $database['body']['$id'];
// Creating collection 1
$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::custom('collection1'),
'name' => ID::custom('collection1'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
// Creating collection 2
$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::custom('collection2'),
'name' => ID::custom('collection2'),
'documentSecurity' => false,
'permissions' => [
Permission::read(Role::user($userId)),
]
]);
$collection3 = $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::custom('collection3'),
'name' => ID::custom('collection3'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
$collection4 = $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::custom('collection4'),
'name' => ID::custom('collection4'),
'documentSecurity' => false,
'permissions' => [
Permission::read(Role::user($userId)),
]
]);
$collection5 = $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::custom('collection5'),
'name' => ID::custom('collection5'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
// Creating one to one relationship from collection 1 to colletion 2
$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' => 'oneToOne',
'twoWay' => false,
'onDelete' => 'setNull',
'key' => $collection2['body']['$id']
]);
// Creating one to one relationship from collection 2 to colletion 3
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection2['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection3['body']['$id'],
'type' => 'oneToOne',
'twoWay' => false,
'onDelete' => 'setNull',
'key' => $collection3['body']['$id']
]);
// Creating one to one relationship from collection 3 to colletion 4
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection3['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection4['body']['$id'],
'type' => 'oneToOne',
'twoWay' => false,
'onDelete' => 'setNull',
'key' => $collection4['body']['$id']
]);
// Creating one to one relationship from collection 4 to colletion 5
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection4['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection5['body']['$id'],
'type' => 'oneToOne',
'twoWay' => false,
'onDelete' => 'setNull',
'key' => $collection5['body']['$id']
]);
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => "Title",
'size' => 100,
'required' => false,
'array' => false,
'default' => null,
]);
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection2['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => "Rating",
'size' => 100,
'required' => false,
'array' => false,
'default' => null,
]);
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection3['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => "Rating",
'size' => 100,
'required' => false,
'array' => false,
'default' => null,
]);
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection4['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => "Rating",
'size' => 100,
'required' => false,
'array' => false,
'default' => null,
]);
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection5['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => "Rating",
'size' => 100,
'required' => false,
'array' => false,
'default' => null,
]);
\sleep(2);
// Creating parent document with a child reference to test the permissions
$parentDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'documentId' => ID::custom($collection1['body']['$id']),
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => ID::custom($collection2['body']['$id']),
'Rating' => '10',
$collection3['body']['$id'] => [
'$id' => ID::custom($collection3['body']['$id']),
'Rating' => '10',
$collection4['body']['$id'] => [
'$id' => ID::custom($collection4['body']['$id']),
'Rating' => '10',
$collection5['body']['$id'] => [
'$id' => ID::custom($collection5['body']['$id']),
'Rating' => '10'
]
]
]
]
]
]);
$this->assertEquals(201, $parentDocument['headers']['status-code']);
// This is the point of the test. We should not need any authorization permission to update the document with same data.
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents/' . $collection1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => ID::custom($collection1['body']['$id']),
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => $collection2['body']['$id'],
'Rating' => '10',
$collection3['body']['$id'] => [
'$id' => $collection3['body']['$id'],
'Rating' => '10',
$collection4['body']['$id'] => [
'$id' => $collection4['body']['$id'],
'Rating' => '10',
$collection5['body']['$id'] => [
'$id' => $collection5['body']['$id'],
'Rating' => '10'
]
]
]
]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($parentDocument['body'], $response['body']);
// Giving update permission of collection 3 to user.
$this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/collection3', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::custom('collection3'),
'name' => ID::custom('collection3'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::update(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
// This is the point of this test. We should be allowed to do this action, and it should not fail on permission check
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents/' . $collection1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => ID::custom($collection2['body']['$id']),
'Rating' => '10',
$collection3['body']['$id'] => [
'$id' => ID::custom($collection3['body']['$id']),
'Rating' => '11',
$collection4['body']['$id'] => [
'$id' => ID::custom($collection4['body']['$id']),
'Rating' => '10',
$collection5['body']['$id'] => [
'$id' => ID::custom($collection5['body']['$id']),
'Rating' => '11'
]
]
]
]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(11, $response['body'][$collection2['body']['$id']]['collection3']['Rating']);
// We should not be allowed to update the document as we do not have permission for collection 2.
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents/' . $collection1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => ID::custom($collection2['body']['$id']),
'Rating' => '11',
$collection3['body']['$id'] => null,
]
]
]);
$this->assertEquals(401, $response['headers']['status-code']);
// We should not be allowed to update the document as we do not have permission for collection 2.
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection2['body']['$id'] . '/documents/' . $collection2['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'Rating' => '11',
]
]);
$this->assertEquals(401, $response['headers']['status-code']);
// Removing update permission from collection 3.
$this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/collection3', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::custom('collection3'),
'name' => ID::custom('collection3'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
// Giving update permission to collection 2.
$this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/collection2', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::custom('collection2'),
'name' => ID::custom('collection2'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::update(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
// Creating collection 3 new document
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection3['body']['$id'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'documentId' => ID::custom('collection3Doc1'),
'data' => [
'Rating' => '20'
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
// We should be allowed to link a new document from collection 3 to collection 2.
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents/' . $collection1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => ID::custom($collection2['body']['$id']),
$collection3['body']['$id'] => 'collection3Doc1',
]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
// We should be allowed to link and create a new document from collection 3 to collection 2.
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents/' . $collection1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => ID::custom($collection2['body']['$id']),
$collection3['body']['$id'] => [
'$id' => ID::custom('collection3Doc2')
],
]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
}
}

View file

@ -120,7 +120,7 @@ trait Base
public static string $GET_TEAM_MEMBERSHIP = 'get_team_membership';
public static string $GET_TEAM_MEMBERSHIPS = 'list_team_memberships';
public static string $CREATE_TEAM_MEMBERSHIP = 'create_team_membership';
public static string $UPDATE_TEAM_MEMBERSHIP_ROLES = 'update_team_membership_roles';
public static string $UPDATE_TEAM_MEMBERSHIP = 'update_team_membership';
public static string $UPDATE_TEAM_MEMBERSHIP_STATUS = 'update_membership_status';
public static string $DELETE_TEAM_MEMBERSHIP = 'delete_team_membership';
@ -1296,9 +1296,9 @@ trait Base
roles
}
}';
case self::$UPDATE_TEAM_MEMBERSHIP_ROLES:
return 'mutation updateTeamMembershipRoles($teamId: String!, $membershipId: String!, $roles: [String!]!){
teamsUpdateMembershipRoles(teamId: $teamId, membershipId: $membershipId, roles: $roles) {
case self::$UPDATE_TEAM_MEMBERSHIP:
return 'mutation updateTeamMembership($teamId: String!, $membershipId: String!, $roles: [String!]!){
teamsUpdateMembership(teamId: $teamId, membershipId: $membershipId, roles: $roles) {
_id
userId
teamId

View file

@ -251,7 +251,7 @@ class TeamsServerTest extends Scope
public function testUpdateTeamMembershipRoles($team, $membership)
{
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$UPDATE_TEAM_MEMBERSHIP_ROLES);
$query = $this->getQuery(self::$UPDATE_TEAM_MEMBERSHIP);
$graphQLPayload = [
'query' => $query,
'variables' => [
@ -268,7 +268,7 @@ class TeamsServerTest extends Scope
$this->assertIsArray($membership['body']['data']);
$this->assertArrayNotHasKey('errors', $membership['body']);
$membership = $membership['body']['data']['teamsUpdateMembershipRoles'];
$membership = $membership['body']['data']['teamsUpdateMembership'];
$this->assertEquals(['developer', 'admin'], $membership['roles']);
}

View file

@ -4,7 +4,6 @@ namespace Tests\E2E\Services\Teams;
use Tests\E2E\Client;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Helpers\ID;
trait TeamsBaseServer
{