This feature will help restore some missing CLI functionality.  It's
also a step toward making sure all our attribute metadata is plugable
with a per-attribute granularity.

See the new module docstring in ipalib/crud.py for details.
>From b8a67200ba1b2b7ce843dda7e3765bc921f03dcb Mon Sep 17 00:00:00 2001
From: Jason Gerard DeRose <jder...@redhat.com>
Date: Thu, 4 Feb 2010 09:52:33 -0700
Subject: [PATCH] Add support for the 'no_create', 'no_update', and 'no_search' Param flags

---
 ipalib/crud.py                 |  112 ++++++++++++++++++++++++++++++++++++++-
 tests/test_ipalib/test_crud.py |    8 ++-
 2 files changed, 114 insertions(+), 6 deletions(-)

diff --git a/ipalib/crud.py b/ipalib/crud.py
index 173fefc..77c97f3 100644
--- a/ipalib/crud.py
+++ b/ipalib/crud.py
@@ -16,14 +16,114 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
 """
 Base classes for standard CRUD operations.
+
+These base classes are for `Method` plugins that provide standard
+Create, Retrieve, Updated, and Delete operations (CRUD) for their corresponding
+`Object` plugin.  In particuar, these base classes provide logic to
+automatically create the plugin args and options by inspecting the params on
+their corresponding `Object` plugin.  This provides a single point of definition
+for LDAP attributes and enforces a simple, consistent API for CRUD operations.
+
+For example, say we want CRUD operations on a hypothetical "user" entry.  First
+we need an `Object` plugin:
+
+>>> from ipalib import Object, Str
+>>> class user(Object):
+...     takes_params = (
+...         Str('login', primary_key=True),
+...         Str('first'),
+...         Str('last'),
+...         Str('ipauniqueid', flags=['no_create', 'no_update']),
+...     )
+...
+
+Next we need `Create`, `Retrieve`, `Updated`, and `Delete` plugins, and
+optionally a `Search` plugin.  For brevity, we'll just define `Create` and
+`Retrieve` plugins:
+
+>>> from ipalib import crud
+>>> class user_add(crud.Create):
+...     pass
+...
+>>> class user_show(crud.Retrieve):
+...     pass
+...
+
+Now we'll register the plugins and finalize the `plugable.API` instance:
+
+>>> from ipalib import create_api
+>>> api = create_api()
+>>> api.register(user)
+>>> api.register(user_add)
+>>> api.register(user_show)
+>>> api.finalize()
+
+First, notice that our ``user`` `Object` has the params we defined with the
+``takes_params`` tuple:
+
+>>> list(api.Object.user.params)
+['login', 'first', 'last', 'ipauniqueid']
+>>> api.Object.user.params.login
+Str('login', primary_key=True)
+
+Although we defined neither ``takes_args`` nor ``takes_options`` for our
+``user_add`` plugin, the `Create` base class automatically generated them for
+us:
+
+>>> list(api.Command.user_add.args)
+['login']
+>>> list(api.Command.user_add.options)
+['first', 'last']
+
+Notice that ``'ipauniqueid'`` isn't included in the options for our ``user_add``
+plugin.  This is because of the ``'no_create'`` flag we used when defining the
+``ipauniqueid`` param.  Often times there are LDAP attributes that are
+automatically created by the server and therefor should not be supplied as an
+option to the `Create` plugin.  Often these same attributes shouldn't be
+update-able either, in which case you can also supply the ``'no_update'`` flag,
+as we did with our ``ipauniqueid`` param.  Lastly, you can also use the ``'no_search'`` flag for attributes that shouldn't be search-able (because, for
+example, the attribute isn't indexed).
+
+As with our ``user_add` plugin, we defined neither ``takes_args`` nor
+``takes_options`` for our ``user_show`` plugin; instead the `Retrieve` base
+class created them for us:
+
+>>> list(api.Command.user_show.args)
+['login']
+>>> list(api.Command.user_show.options)
+[]
+
+As you can see, `Retrieve` plugins take a single argument (the primary key) and
+no options.  If needed, you can still specify options for your `Retrieve` plugin
+with a ``takes_options`` tuple.
+
+Flags like ``'no_create'`` remove LDAP attributes from those that can be
+supplied as *input* to a `Method`, but they don't effect the attributes that can
+be returned as *output*.  Regardless of what flags have been used, the output
+entry (or list of entries) can contain all the attributes defined on the
+`Object` plugin (in our case, the above ``user.params``).
+
+For example, compare ``user.params`` with ``user_add.output_params`` and
+``user_show.output_params``:
+
+>>> list(api.Object.user.params)
+['login', 'first', 'last', 'ipauniqueid']
+>>> list(api.Command.user_add.output_params)
+['login', 'first', 'last', 'ipauniqueid']
+>>> list(api.Command.user_show.output_params)
+['login', 'first', 'last', 'ipauniqueid']
+
+Note that the above are all equal.
 """
 
+from frontend import Method, Object
 import backend, frontend, parameters, output
 
 
-class Create(frontend.Method):
+class Create(Method):
     """
     Create a new entry.
     """
@@ -39,13 +139,15 @@ class Create(frontend.Method):
             for option in super(Create, self).get_options():
                 yield option
         for option in self.obj.params_minus(self.args):
+            if 'no_create' in option.flags:
+                continue
             yield option.clone(attribute=True)
         if not self.extra_options_first:
             for option in super(Create, self).get_options():
                 yield option
 
 
-class PKQuery(frontend.Method):
+class PKQuery(Method):
     """
     Base class for `Retrieve`, `Update`, and `Delete`.
     """
@@ -75,6 +177,8 @@ class Update(PKQuery):
             for option in super(Update, self).get_options():
                 yield option
         for option in self.obj.params_minus_pk():
+            if 'no_update' in option.flags:
+                continue
             yield option.clone(attribute=True, required=False, autofill=False)
         if not self.extra_options_first:
             for option in super(Update, self).get_options():
@@ -89,7 +193,7 @@ class Delete(PKQuery):
     has_output = output.standard_delete
 
 
-class Search(frontend.Method):
+class Search(Method):
     """
     Retrieve all entries that match a given search criteria.
     """
@@ -104,6 +208,8 @@ class Search(frontend.Method):
             for option in super(Search, self).get_options():
                 yield option
         for option in self.obj.params_minus(self.args):
+            if 'no_search' in option.flags:
+                continue
             yield option.clone(
                 attribute=True, query=True, required=False, autofill=False
             )
diff --git a/tests/test_ipalib/test_crud.py b/tests/test_ipalib/test_crud.py
index 47d51c5..969fb4f 100644
--- a/tests/test_ipalib/test_crud.py
+++ b/tests/test_ipalib/test_crud.py
@@ -23,6 +23,7 @@ Test the `ipalib.crud` module.
 
 from tests.util import read_only, raises, get_api, ClassChecker
 from ipalib import crud, frontend, plugable, config
+from ipalib.parameters import Str
 
 
 class CrudChecker(ClassChecker):
@@ -38,9 +39,10 @@ class CrudChecker(ClassChecker):
         class user(frontend.Object):
             takes_params = (
                 'givenname',
-                'sn',
-                frontend.Param('uid', primary_key=True),
+                Str('sn', flags='no_update'),
+                Str('uid', primary_key=True),
                 'initials',
+                Str('uidnumber', flags=['no_create', 'no_search'])
             )
         class user_verb(self.cls):
             takes_args = args
@@ -102,7 +104,7 @@ class test_Update(CrudChecker):
         """
         api = self.get_api()
         assert list(api.Method.user_verb.options) == \
-            ['givenname', 'sn', 'initials']
+            ['givenname', 'initials', 'uidnumber']
         for param in api.Method.user_verb.options():
             assert param.required is False
 
-- 
1.6.3.3

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to