def test_bearer_token_valid_via_headers(self, request): user = M.User.by_username('test-admin') consumer_token = M.OAuthConsumerToken( name='foo', description='foo app', ) request_token = M.OAuthRequestToken( consumer_token_id=consumer_token._id, user_id=user._id, callback='manual', validation_pin=h.nonce(20), is_bearer=True, ) access_token = M.OAuthAccessToken( consumer_token_id=consumer_token._id, request_token_id=request_token._id, user_id=user._id, is_bearer=True, ) ThreadLocalODMSession.flush_all() token = access_token.api_key request.headers = { 'Authorization': 'Bearer {}'.format(token) } request.scheme = 'https' r = self.api_post('/rest/p/test/wiki', access_token='foo', status=200) # reverse proxy situation request.scheme = 'http' request.environ['paste.testing'] = False request.environ['HTTP_X_FORWARDED_PROTOx'] = 'https' r = self.api_post('/rest/p/test/wiki', access_token='foo', status=200)
def test_bearer_token_valid_via_headers(self, request): user = M.User.by_username('test-admin') consumer_token = M.OAuthConsumerToken( name='foo', description='foo app', ) request_token = M.OAuthRequestToken( consumer_token_id=consumer_token._id, user_id=user._id, callback='manual', validation_pin=h.nonce(20), is_bearer=True, ) access_token = M.OAuthAccessToken( consumer_token_id=consumer_token._id, request_token_id=request_token._id, user_id=user._id, is_bearer=True, ) ThreadLocalODMSession.flush_all() token = access_token.api_key request.headers = {'Authorization': 'Bearer {}'.format(token)} request.scheme = 'https' r = self.api_post('/rest/p/test/wiki', access_token='foo', status=200) # reverse proxy situation request.scheme = 'http' request.environ['paste.testing'] = False request.environ['HTTP_X_FORWARDED_PROTOx'] = 'https' r = self.api_post('/rest/p/test/wiki', access_token='foo', status=200)
def test_bearer_token_valid(self, request): user = M.User.by_username('test-admin') consumer_token = M.OAuthConsumerToken( name='foo', description='foo app', ) request_token = M.OAuthRequestToken( consumer_token_id=consumer_token._id, user_id=user._id, callback='manual', validation_pin=h.nonce(20), is_bearer=True, ) access_token = M.OAuthAccessToken( consumer_token_id=consumer_token._id, request_token_id=request_token._id, user_id=user._id, is_bearer=True, ) ThreadLocalODMSession.flush_all() request.headers = {} request.params = {'access_token': access_token.api_key} request.scheme = 'https' r = self.api_post('/rest/p/test/wiki', access_token='foo') assert_equal(r.status_int, 200)
def generate_wiki_thread(test): # automagically instantiate the app test.app.get('/wiki/') project = M.Project.query.get(shortname='test') app = project.app_instance('wiki') page = WM.Page.query.get(app_config_id=app.config._id, title='Home') thread = page.discussion_thread # create a few posts by a few users with push_config(c, user=M.User.query.get(username='******'), app=app, project=project): thread.add_post(text='This is very helpful') thread.add_post(text="But it's not **super** helpful") with push_config(c, user=M.User.query.get(username='******')): thread.add_post(text='I disagree') with push_config(c, user=M.User.query.get(username='******')): thread.add_post(text='But what about foo?') ThreadLocalODMSession.flush_all() url = '/p/test/wiki/_discuss/thread/{}/'.format(thread._id) return url
def test_project_data(self): project = M.Project.query.get(shortname='test') project.summary = 'A Summary' project.short_description = 'A Short Description' ThreadLocalODMSession.flush_all() r = self.app.get('/rest/p/test?doap') assert_equal(r.content_type, 'application/rdf+xml') p = r.xml.find(self.ns + 'Project') assert_equal(p.attrib[self.rdf + 'about'], 'http://localhost/rest/p/test?doap#') assert_equal(p.find(self.ns + 'name').text, 'test') assert_equal(p.find(self.dc + 'title').text, 'Test Project') assert_equal(p.find(self.ns_sf + 'private').text, '0') assert_equal(p.find(self.ns + 'shortdesc').text, 'A Summary') assert_equal( p.find(self.ns + 'description').text, 'A Short Description') assert_equal( p.find(self.ns + 'created').text, project._id.generation_time.strftime('%Y-%m-%d')) maintainers = p.findall(self.ns + 'maintainer') assert_equal(len(maintainers), 1) user = maintainers[0].find(self.foaf + 'Person') assert_equal(user.find(self.foaf + 'name').text, 'Test Admin') assert_equal(user.find(self.foaf + 'nick').text, 'test-admin') assert_equal( list(user.find(self.foaf + 'homepage').items())[0][1], 'http://localhost/u/test-admin/')
def test_delete_item_success(self): activity_data = { "obj": { "activity_extras": { "summary": "Sensitive private info, oops" }, "activity_url": "/p/test/tickets/34/?limit=25#ed7c", "activity_name": "a comment" }, "target": { "activity_extras": { "allura_id": "Ticket:529f57a6033c5e5985db2efa", "summary": "Make activitystream timeline look better" }, "activity_url": "/p/test/tickets/34/", "activity_name": "ticket #34" }, "actor": { "activity_extras": { "icon_url": "/u/test-admin/user_icon", "allura_id": "User:521f96cb033c5e2587adbdff" }, "activity_url": "/u/test-admin/", "activity_name": "Administrator 1", "node_id": "User:521f96cb033c5e2587adbdff" }, "verb": "posted", "published": dateutil.parser.parse("2013-12-04T21:48:19.817"), "score": 1386193699, "node_id": "Project:527a6584033c5e62126f5a60", "owner_id": "Project:527a6584033c5e62126f5a60" } activity = Activity(**activity_data) activity2 = Activity( **dict(activity_data, node_id='Project:123', owner_id='User:456')) activity3 = Activity( **dict(activity_data, node_id='User:abc', owner_id='User:abc')) ThreadLocalODMSession.flush_all() activity_id = str(activity._id) assert_equal( Activity.query.find({ 'obj.activity_extras.summary': 'Sensitive private info, oops' }).count(), 3) self.app.post( '/u/test-user-1/activity/delete_item', {'activity_id': activity_id}, extra_environ={'username': str('root')}, # nbhd admin status=200) ThreadLocalODMSession.flush_all() assert_equal( Activity.query.find({ 'obj.activity_extras.summary': 'Sensitive private info, oops' }).count(), 0)
def connection_configure(): from ming import configure from ming.odm import ThreadLocalODMSession configure(**{'ming.mysession.uri': 'mongodb://localhost:27017/tutorial'}) session = ThreadLocalODMSession.by_name('mysession') session.db ThreadLocalODMSession.by_name('mysession') is session
def main(args): test = TestController() setup(test) url = generate_wiki_thread(test) ThreadLocalODMSession.close_all() # make sure ODM sessions won't get re-used counts = count_page(test, url, verbose=args.verbose, debug_html=args.debug_html) print json.dumps(counts) write_csv(counts, args.id, args.data_file) test.tearDown()
def main(args): test = TestController() setup(test) url = generate_wiki_thread(test) # make sure ODM sessions won't get re-used ThreadLocalODMSession.close_all() counts = count_page(test, url, verbose=args.verbose, debug_html=args.debug_html) print(json.dumps(counts)) write_csv(counts, args.id, args.data_file) test.tearDown()
def __call__(self, environ, start_response): try: result = self.app(environ, start_response) if isinstance(result, list): self._cleanup_request() return result else: return self._cleanup_iterator(result) except self.flush_on_errors as exc: self._cleanup_request() raise except: ThreadLocalODMSession.close_all() ContextualODMSession.close_all(self._context_func()) raise
def create_session(login, password, path): global session if session == None: session = ThreadLocalODMSession( bind=create_datastore('mongodb://%s:%s@%s' % (login, password, path))) return session
def get_session(self): if 'session_uri' not in self.config_values: self.set_session_uri("mongodb://localhost:27017/mldatahub") if self.session is None: self.session = ThreadLocalODMSession(bind=create_datastore(self.config_values['session_uri'])) return self.session
def _disable_users(self, disable): dev = M.User.by_username('test-user') self.proj.add_user(dev, ['Developer']) ThreadLocalODMSession.flush_all() g.credentials.clear() proj = u'p/{}'.format(self.p_shortname) msg = u'Account disabled because project /{} is deleted. Reason: The Reason'.format(proj) opts = ['-r', 'The Reason', proj] if disable: opts.insert(0, '--disable-users') _audit = audits if disable else out_audits with _audit(msg): self.run_script(opts) admin = M.User.by_username('test-admin') dev = M.User.by_username('test-user') assert admin.disabled is disable assert dev.disabled is disable
def test_delete_item_success(self): activity_data = { "obj": { "activity_extras": { "summary": "Sensitive private info, oops" }, "activity_url": "/p/test/tickets/34/?limit=25#ed7c", "activity_name": "a comment" }, "target": { "activity_extras": { "allura_id": "Ticket:529f57a6033c5e5985db2efa", "summary": "Make activitystream timeline look better" }, "activity_url": "/p/test/tickets/34/", "activity_name": "ticket #34" }, "actor": { "activity_extras": { "icon_url": "/u/test-admin/user_icon", "allura_id": "User:521f96cb033c5e2587adbdff" }, "activity_url": "/u/test-admin/", "activity_name": "Administrator 1", "node_id": "User:521f96cb033c5e2587adbdff" }, "verb": "posted", "published": dateutil.parser.parse("2013-12-04T21:48:19.817"), "score": 1386193699, "node_id": "Project:527a6584033c5e62126f5a60", "owner_id": "Project:527a6584033c5e62126f5a60" } activity = Activity(**activity_data) activity2 = Activity(**dict(activity_data, node_id='Project:123', owner_id='User:456')) activity3 = Activity(**dict(activity_data, node_id='User:abc', owner_id='User:abc')) ThreadLocalODMSession.flush_all() activity_id = str(activity._id) assert_equal(Activity.query.find({'obj.activity_extras.summary': 'Sensitive private info, oops'}).count(), 3) self.app.post('/u/test-user-1/activity/delete_item', {'activity_id': activity_id}, extra_environ={'username': '******'}, # nbhd admin status=200) ThreadLocalODMSession.flush_all() assert_equal(Activity.query.find({'obj.activity_extras.summary': 'Sensitive private info, oops'}).count(), 0)
def _disable_users(self, disable): dev = M.User.by_username('test-user') self.proj.add_user(dev, ['Developer']) ThreadLocalODMSession.flush_all() g.credentials.clear() proj = u'p/{}'.format(self.p_shortname) msg = u'Account disabled because project /{} is deleted. Reason: The Reason'.format(proj) opts = ['-r', 'The Reason', proj] if disable: opts.insert(0, '--disable-users') _audit = audits if disable else out_audits with _audit(msg, user=True): self.run_script(opts) admin = M.User.by_username('test-admin') dev = M.User.by_username('test-user') assert admin.disabled is disable assert dev.disabled is disable
def test_bearer_token_valid(self, request): user = M.User.by_username("test-admin") consumer_token = M.OAuthConsumerToken(name="foo", description="foo app") request_token = M.OAuthRequestToken( consumer_token_id=consumer_token._id, user_id=user._id, callback="manual", validation_pin=h.nonce(20), is_bearer=True, ) access_token = M.OAuthAccessToken( consumer_token_id=consumer_token._id, request_token_id=request_token._id, user_id=user._id, is_bearer=True ) ThreadLocalODMSession.flush_all() request.params = {"access_token": access_token.api_key} request.scheme = "https" r = self.api_post("/rest/p/test/wiki", access_token="foo") assert_equal(r.status_int, 200)
def connection_session(): from ming import create_datastore from ming.odm import ThreadLocalODMSession session = ThreadLocalODMSession( bind=create_datastore('mongodb://localhost:27017/tutorial') ) session # The database and datastore are still available # through the session as .db and .bind session.db session.bind
def flush_context(): ThreadLocalODMSession.close_all() try: yield ThreadLocalODMSession.flush_all() finally: ThreadLocalODMSession.close_all()
def generate_wiki_thread(test): # automagically instantiate the app test.app.get("/wiki/") project = M.Project.query.get(shortname="test") app = project.app_instance("wiki") page = WM.Page.query.get(app_config_id=app.config._id, title="Home") thread = page.discussion_thread # create a few posts by a few users with push_config(c, user=M.User.query.get(username="******"), app=app, project=project): thread.add_post(text="This is very helpful") thread.add_post(text="But it's not **super** helpful") with push_config(c, user=M.User.query.get(username="******")): thread.add_post(text="I disagree") with push_config(c, user=M.User.query.get(username="******")): thread.add_post(text="But what about foo?") ThreadLocalODMSession.flush_all() url = "/p/test/wiki/_discuss/thread/{}/".format(thread._id) return url
def test_project_data(self): project = M.Project.query.get(shortname='test') project.summary = 'A Summary' project.short_description = 'A Short Description' ThreadLocalODMSession.flush_all() r = self.app.get('/rest/p/test?doap') assert_equal(r.content_type, 'application/rdf+xml') p = r.xml.find(self.ns + 'Project') assert_equal(p.attrib[self.rdf + 'about'], 'http://localhost/rest/p/test?doap#') assert_equal(p.find(self.ns + 'name').text, 'test') assert_equal(p.find(self.dc + 'title').text, 'Test Project') assert_equal(p.find(self.ns_sf + 'private').text, '0') assert_equal(p.find(self.ns + 'shortdesc').text, 'A Summary') assert_equal(p.find(self.ns + 'description').text, 'A Short Description') assert_equal(p.find(self.ns + 'created').text, project._id.generation_time.strftime('%Y-%m-%d')) maintainers = p.findall(self.ns + 'maintainer') assert_equal(len(maintainers), 1) user = maintainers[0].find(self.foaf + 'Person') assert_equal(user.find(self.foaf + 'name').text, 'Test Admin') assert_equal(user.find(self.foaf + 'nick').text, 'test-admin') assert_equal(user.find(self.foaf + 'homepage').items()[0][1], 'http://localhost/u/test-admin/')
def setUp(self): self.datastore = create_datastore('mim:///test_db') self.session = ThreadLocalODMSession(Session(bind=self.datastore)) class Parent(MappedClass): class __mongometa__: name = 'parent' session = self.session _id = FieldProperty(S.ObjectId) Mapper.compile_all() self.Parent = Parent self.create_app = TestApp(MingMiddleware(self._wsgi_create_object)) self.remove_app = TestApp(MingMiddleware(self._wsgi_remove_object)) self.remove_exc = TestApp(MingMiddleware(self._wsgi_remove_object_exc))
def __init__(self): self.ming_session = Session() self.DBSession = ThreadLocalODMSession(self.ming_session) class User(MappedClass): class __mongometa__: session = self.DBSession name = 'tg_user' unique_indexes = [('user_name',), ('email_address',)] _id = FieldProperty(s.ObjectId) user_name = FieldProperty(s.String) email_address = FieldProperty(s.String) display_name = FieldProperty(s.String) password = FieldProperty(s.String) self.User = User
def setUp(self): self.datastore = create_datastore('mim:///test_db') self.session = ThreadLocalODMSession(Session(bind=self.datastore)) class TestRelationParent(MappedClass): """This class _must_ have a unique class name or it will conflict with other tests.""" class __mongometa__: name = 'parent' session = self.session _id = FieldProperty(S.ObjectId) Mapper.compile_all() self.Parent = TestRelationParent self.create_app = TestApp(MingMiddleware(self._wsgi_create_object)) self.remove_app = TestApp(MingMiddleware(self._wsgi_remove_object)) self.remove_exc = TestApp(MingMiddleware(self._wsgi_remove_object_exc))
def __init__(self): self.ming_session = Session() self.DBSession = ThreadLocalODMSession(self.ming_session) class User(MappedClass): class __mongometa__: session = self.DBSession name = 'tg_user' unique_indexes = [('user_name', ), ('email_address', )] _id = FieldProperty(s.ObjectId) user_name = FieldProperty(s.String) email_address = FieldProperty(s.String) display_name = FieldProperty(s.String) password = FieldProperty(s.String) @classmethod def by_user_name(cls, user_name): return cls.query.find({'user_name': user_name}).one() self.User = User
#!/usr/bin/env python # -*- coding: utf-8 -*- # Clear the class names in case MappedClasses are declared in another example from ming.odm import Mapper Mapper._mapper_by_classname.clear() from ming import create_datastore from ming.odm import ThreadLocalODMSession session = ThreadLocalODMSession(bind=create_datastore('odm_tutorial')) from ming import schema from ming.odm import MappedClass from ming.odm import FieldProperty, ForeignIdProperty, RelationProperty class Transport(MappedClass): class __mongometa__: session = session name = 'transport' polymorphic_on = '_type' polymorphic_identity = 'base' _id = FieldProperty(schema.ObjectId) origin = FieldProperty(schema.String(required=True)) destination = FieldProperty(schema.String(if_missing='')) _type = FieldProperty(schema.String(if_missing='base')) def move(self): return 'moving from {} to {}'.format(self.origin, self.destination)
def _deny(obj, role, perm): obj.acl.insert(0, M.ACE.deny(role._id, perm)) ThreadLocalODMSession.flush_all() Credentials.get().clear()
def _add_to_group(user, role): M.ProjectRole.by_user(user, upsert=True).roles.append(role._id) ThreadLocalODMSession.flush_all() Credentials.get().clear()
def _cleanup_request(): ThreadLocalODMSession.flush_all() ThreadLocalODMSession.close_all()
#{initial-imports from ming import Session from ming.datastore import DataStore from ming.odm import ThreadLocalODMSession bind = DataStore('mongodb://localhost:27017/', database='odm_tutorial') doc_session = Session(bind) session = ThreadLocalODMSession(doc_session=doc_session) #} #{odm-imports from ming import schema from ming.odm import FieldProperty, ForeignIdProperty, RelationProperty from ming.odm import Mapper from ming.odm.declarative import MappedClass #} class WikiPage(MappedClass): class __mongometa__: session = session name = 'wiki_page' _id = FieldProperty(schema.ObjectId) title = FieldProperty(str) text = FieldProperty(str) comments=RelationProperty('WikiComment') class WikiComment(MappedClass):
def _cleanup_request(self): context = self._context_func() ThreadLocalODMSession.flush_all() ContextualODMSession.flush_all(context) ThreadLocalODMSession.close_all() ContextualODMSession.close_all(context)
from ming import create_datastore from ming.odm import ThreadLocalODMSession from api import DATABASE session = ThreadLocalODMSession(bind=create_datastore( f'mongodb://{DATABASE.user}:{DATABASE.password}@' f'{DATABASE.host}:{DATABASE.port}/{DATABASE.dbname}?authSource=admin'))
self.flush_on_errors = flush_on_errors self._context_func = context_func or (lambda:None) def __call__(self, environ, start_response): try: result = self.app(environ, start_response) if isinstance(result, list): self._cleanup_request() return result else: return self._cleanup_iterator(result) except self.flush_on_errors, exc: self._cleanup_request() raise except: ThreadLocalODMSession.close_all() ContextualODMSession.close_all(self._context_func()) raise def _cleanup_request(self): context = self._context_func() ThreadLocalODMSession.flush_all() ContextualODMSession.flush_all(context) ThreadLocalODMSession.close_all() ContextualODMSession.close_all(context) def _cleanup_iterator(self, result): for x in result: yield x self._cleanup_request()
import time import traceback from datetime import datetime import pymongo from ming import collection, Session, Field, Index from ming import schema as S from ming.utils import LazyProperty from ming.odm import ThreadLocalODMSession, session doc_session = Session.by_name('monq') odm_session = ThreadLocalODMSession(doc_session) STATES=('ready', 'busy', 'error', 'complete') RESULT_TYPES=('keep', 'forget') def task(func): '''Decorator to add some methods to task functions''' def post(*args, **kwargs): return TaskObject.post(func, args, kwargs) func.post = post return func TaskDoc = collection( 'monq.task', doc_session, Field('_id', S.ObjectId), Field('state', S.OneOf(*STATES)), Field('priority', int), Field('result_type', S.OneOf(*RESULT_TYPES)), Field('time', dict(
from ming.odm import ThreadLocalODMSession, Mapper from ming.odm import ForeignIdProperty, RelationProperty from lesson_2_0 import model as M sess = ThreadLocalODMSession(M.sess) class Forum(object): pass class Thread(object): pass class Post(object): pass sess.mapper(Forum, M.Forum, properties=dict( threads=RelationProperty('Thread'))) sess.mapper(Thread, M.Thread, properties=dict( forum_id=ForeignIdProperty('Forum'), forum=RelationProperty('Forum'), posts=RelationProperty('Post'))) sess.mapper(Post, M.Post, properties=dict( forum_id=ForeignIdProperty('Forum'), thread_id=ForeignIdProperty('Thread'), forum=RelationProperty('Forum'), thread=RelationProperty('Thread'))) Mapper.compile_all() class Product(object):
def ming_session(): return ThreadLocalODMSession( bind=create_datastore(os.getenv("MONGODB_URL")), )
from ming.odm import ThreadLocalODMSession, Mapper from ming.odm import ForeignIdProperty, RelationProperty from lesson_2_0 import model as M sess = ThreadLocalODMSession(M.sess) class Forum(object): pass class Thread(object): pass class Post(object): pass sess.mapper(Forum, M.Forum, properties=dict(threads=RelationProperty('Thread'))) sess.mapper(Thread, M.Thread, properties=dict(forum_id=ForeignIdProperty('Forum'), forum=RelationProperty('Forum'), posts=RelationProperty('Post'))) sess.mapper(Post, M.Post, properties=dict(forum_id=ForeignIdProperty('Forum'),
def _add_to_group(user, role): user.project_role().roles.append(role._id) ThreadLocalODMSession.flush_all() Credentials.get().clear()
# --- --- -- --- --- --- # # --- Import Modules --- # # --- --- -- --- --- --- # import pymongo from pymongo import MongoClient from bson.objectid import ObjectId from ming import create_datastore from ming.odm import ThreadLocalODMSession from ming import schema from ming.odm import MappedClass from ming.odm import FieldProperty, ForeignIdProperty session = ThreadLocalODMSession( bind=create_datastore('mongodb://localhost:27017')) db = session.db collections = {'users': db.users, 'people': db.people}
self.flush_on_errors = flush_on_errors self._context_func = context_func or (lambda: None) def __call__(self, environ, start_response): try: result = self.app(environ, start_response) if isinstance(result, list): self._cleanup_request() return result else: return self._cleanup_iterator(result) except self.flush_on_errors, exc: self._cleanup_request() raise except: ThreadLocalODMSession.close_all() ContextualODMSession.close_all(self._context_func()) raise def _cleanup_request(self): context = self._context_func() ThreadLocalODMSession.flush_all() ContextualODMSession.flush_all(context) ThreadLocalODMSession.close_all() ContextualODMSession.close_all(context) def _cleanup_iterator(self, result): for x in result: yield x self._cleanup_request()
from ming import Session from ming.odm import ThreadLocalODMSession mainsession = Session() DBSession = ThreadLocalODMSession(mainsession) """:type :ming.odm.ODMSession """
from ming import create_datastore from ming.odm import ThreadLocalODMSession DB_PATH = 'mongodb://localhost:27017/library' SESSION = ThreadLocalODMSession(bind=create_datastore(DB_PATH))
from ming import schema from ming.odm import FieldProperty from ming.odm import ForeignIdProperty from ming.odm import RelationProperty from ming.odm.declarative import MappedClass import ming from ming.odm.mapper import MapperExtension from ming.schema import ObjectId config = configparser.ConfigParser() config.read('C://Users//Zerbs//accounts.sec') session = ThreadLocalODMSession( bind=create_datastore('mongodb://%s:%s@%s' % (config["mongo"]["login"], config["mongo"]["password"], config["mongo"]["path"])) ) class MyExtension(MapperExtension): def before_insert(self, obj, st, sess): print("--"*20) #print(str(obj.worker_id)) print(obj) if (TWorker.query.find().all()[0].get_by_id(str(obj.worker_id)) == None): raise ValueError("Worker id must match an id in table with workers") premium_size = TPremiumSize.query.find().all()[0].get_by_id(str(obj.premium_id)) if (premium_size == None): raise ValueError("Premium id must match an id in table with premium sizes")
from ming import create_datastore from ming.odm import ThreadLocalODMSession from ming import schema from ming.odm import MappedClass from ming.odm import FieldProperty from database import datastore session = ThreadLocalODMSession(bind=create_datastore(datastore)) class Group(MappedClass): class __mongometa__: session = session name = 'groups' # indexes = ['author.name', 'comments.author.name'] _id = FieldProperty(schema.ObjectId) name = FieldProperty(schema.String, unique=True) item_order = FieldProperty([schema.String]) class Item(MappedClass): class __mongometa__: session = session name = 'items' _id = FieldProperty(schema.ObjectId) name = FieldProperty(schema.String, unique=True) values = FieldProperty([]) groups = FieldProperty([schema.String])