diff --git a/.gitignore b/.gitignore index 3980740cf..fec488244 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,4 @@ env venv *.sublime* .vscode/ - +.tox diff --git a/README.rst b/README.rst index cfda3f138..cdb693cea 100644 --- a/README.rst +++ b/README.rst @@ -97,6 +97,13 @@ Includes: - Related Select2 fields. - Google charts with automatic group by or direct values and filters. - AddOn system, write your own and contribute. + - CRUD REST API + - Automatic CRUD RESTful APIs. + - Internationalization + - Integration with flask-jwt-extended extension to protect your endpoints. + - Metadata for dynamic rendering. + - Selectable columns and metadata keys. + - Automatic and configurable data validation. - Forms - Automatic, Add, Edit and Show from Database Models - Labels and descriptions for each field. diff --git a/docs/api.rst b/docs/api.rst index 3883d9e5b..8e84aec48 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -20,6 +20,7 @@ flask_appbuilder.security.decorators .. automodule:: flask_appbuilder.security.decorators + .. autofunction:: protect .. autofunction:: has_access .. autofunction:: permission_name @@ -30,6 +31,27 @@ flask_appbuilder.models.decorators .. autofunction:: renders +flask_appbuilder.api +============================== + +.. automodule:: flask_appbuilder.api + + .. autofunction:: expose + .. autofunction:: rison + .. autofunction:: safe + +BaseApi +------- + +.. autoclass:: BaseApi + :members: + +ModelRestApi +------------ + +.. autoclass:: ModelRestApi + :members: + flask_appbuilder.baseviews ============================== diff --git a/docs/config.rst b/docs/config.rst index cc300a1bc..f3d5fdc2c 100755 --- a/docs/config.rst +++ b/docs/config.rst @@ -194,6 +194,13 @@ Use config.py to configure the following parameters. By default it will use SQLL | | the existing languages with the countries | | | | name and flag | | +-----------------------------------+--------------------------------------------+-----------+ +| FAB_API_SHOW_STACKTRACE | Sends api stack trace on uncaught | No | +| | exceptions. (Boolean) | | ++-----------------------------------+--------------------------------------------+-----------+ +| FAB_API_MAX_PAGE_SIZE | Sets a limit for FAB Model Api page size | No | ++-----------------------------------+--------------------------------------------+-----------+ +| FAB_API_SWAGGER_UI | Enables a Swagger UI view (Boolean) | No | ++-----------------------------------+--------------------------------------------+-----------+ Using config.py diff --git a/docs/images/swagger001.png b/docs/images/swagger001.png new file mode 100644 index 000000000..2882f5623 Binary files /dev/null and b/docs/images/swagger001.png differ diff --git a/docs/images/swagger002.png b/docs/images/swagger002.png new file mode 100644 index 000000000..17ba3c70e Binary files /dev/null and b/docs/images/swagger002.png differ diff --git a/docs/index.rst b/docs/index.rst index 323716a2d..d3d6382bc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -35,6 +35,7 @@ Contents: views quickhowto + rest_api quickhowto_mongo quickcharts quickfiles diff --git a/docs/quickhowto.rst b/docs/quickhowto.rst index f5dd4ece6..546b04ebf 100644 --- a/docs/quickhowto.rst +++ b/docs/quickhowto.rst @@ -284,6 +284,10 @@ in case of success or errors. See the following table for a description of each REST API -------- +note: + This sort of automatic REST API is going to be deprecated, and will + be completely removed in 2 minors. + This API is still BETA and will be subject to change. In the future F.A.B. will probably use AngularJS to display the UI interface using AJAX. diff --git a/docs/rest_api.rst b/docs/rest_api.rst new file mode 100644 index 000000000..4ea9b857a --- /dev/null +++ b/docs/rest_api.rst @@ -0,0 +1,1372 @@ +REST API +======== + +On this chapter we are going to describe how you can define a RESTful API +using almost the same concept as defining your MVC views. + +:note: + Follow this example on Flask-AppBuilder project ./examples/base_api/ + +First let's see a basic example on how you can define your own +custom API endpoints:: + + + from flask_appbuilder.api import BaseApi, expose + from . import appbuilder + + + class ExampleApi(BaseApi): + def greeting(self): + return self.response(200, message="Hello") + + + appbuilder.add_api(ExampleApi) + + +On the previous example, we are exposing an HTTP GET endpoint, +that returns the following JSON payload:: + + + { + "message": "Hello" + } + +The ``@expose`` decorator registers your class method as a Flask route that is going +to be associated with a Flask blueprint. A ``BaseApi`` class defines a blueprint that +contains all exposed methods. By default the base route of the class blueprint is +defined by: + +``/api/v1/`` + +So we can make a request to our method using:: + + $ curl http://localhost:8080/api/v1/exampleapi/greeting + +To override the base route class blueprint, override the ``base_route`` property, +so on our previous example:: + + from flask_appbuilder.api import BaseApi, expose + from . import appbuilder + + + class ExampleApi(BaseApi): + + base_route = '/newapi/v2/nice' + + @expose('/greeting') + def greeting(self): + return self.response(200, message="Hello") + + + appbuilder.add_api(ExampleApi) + +Now our endpoint will be:: + + $ curl http://localhost:8080/newapi/v2/nice/greeting + +We can also just override the version and/or resource name, +using ``version`` and ``resource_name`` properties:: + + from flask_appbuilder.api import BaseApi, expose + from . import appbuilder + + + class ExampleApi(BaseApi): + + resource_name = 'example' + + @expose('/greeting') + def greeting(self): + return self.response(200, message="Hello") + + + appbuilder.add_api(ExampleApi) + +Now our endpoint will be:: + + $ curl http://localhost:8080/api/v1/example/greeting + + +The other HTTP methods (PUT, POST, DELETE, ...) can be defined just like +a Flask route signature:: + + from flask import request + from flask_appbuilder.api import BaseApi, expose + + class ExampleApi(BaseApi): + + .... + + @expose('/greeting2', methods=['POST', 'GET']) + def greeting2(self): + if request.method == 'GET': + return self.response(200, message="Hello (GET)") + return self.response(201, message="Hello (POST)") + +The previous example will expose a new `greeting2` endpoint on HTTP GET and POST +so we can request it by:: + + $ curl http://localhost:8080/api/v1/example/greeting2 + { + "message": "Hello (GET)" + } + $ curl -XPOST http://localhost:8080/api/v1/example/greeting2 + { + "message": "Hello (POST)" + } + + +Let's make our method a bit more interesting, and send our name on the HTTP +GET method. You can optionally use a ``@rison`` decorator that will parse +the HTTP URI arguments from a *Rison* structure to a python data structure. +On this example it may seem a bit overboard but with *Rison* we can handle +complex HTTP GET arguments in a human readable and predictable way. +*Rison* is a slight variation of JSON that looks vastly superior after URI encoding. +Rison still expresses exactly the same set of data structures as JSON, +so data can be translated back and forth without loss or guesswork:: + + from flask_appbuilder.api import BaseApi, expose, rison + + class ExampleApi(BaseApi): + + ... + + @expose('/greeting3') + @rison() + def greeting3(self, **kwargs): + if 'name' in kwargs['rison']: + return self.response( + 200, + message="Hello {}".format(kwargs['rison']['name']) + ) + return self.response_400(message="Please send your name") + +And to test our method:: + + $ curl 'http://localhost:8080/api/v1/example/greeting3?q=(name:daniel)' + { + "message": "Hello daniel" + } + +To test this concept let's create a new method where we send a somewhat complex +data structure that will use numbers, booleans and lists, and send it back JSON formatted. +First our data structure, let's first think JSON:: + + { + "bool": true, + "list": ["a", "b", "c"], + "number": 777, + "string": "string" + "null": null + } + +On *Rison* format:: + + (bool:!t,list:!(a,b,c),null:!n,number:777,string:'string') + +Behind the scenes FAB is using *prison* a very nicely done fork developed by @betodealmeida +We can use this package, to help us dump or load python structures to Rison:: + + import prison + b = { + "bool": True, + "list": ["a", "b", "c"], + "number": 777, + "string": "string", + "null": None + } + + print(prison.dumps(b)) + +So to test our concept:: + + ... + + @expose('/risonjson') + @rison() + def rison_json(self, **kwargs): + return self.response(200, result=kwargs['rison']) + +Then call it:: + + $ curl 'http://localhost:8080/api/v1/example/risonjson?q=(bool:!t,list:!(a,b,c),null:!n,number:777,string:'string')' + { + "result": { + "bool": true, + "list": [ + "a", + "b", + "c" + ], + "null": null, + "number": 777, + "string": "string" + } + } + + +Notice how the data types are preserved. Remember that we are building a Flask app +so you can always use *normal* URI arguments using Flask's ``request.args`` + +If we send an invalid *Rison* argument we get an error:: + + $ curl -v 'http://localhost:8080/api/v1/example/risonjson?q=(bool:!t' + ... + < HTTP/1.0 400 BAD REQUEST + < Content-Type: application/json; charset=utf-8 + ... + { + "message": "Not a valid rison argument" + } + +You can additionally pass a JSON schema to +validate your Rison arguments, this way you can implement a very strict API easily:: + + schema = { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + ... + + @expose('/greeting4') + @rison(schema) + def greeting4(self, **kwargs): + return self.response( + 200, + message="Hello {}".format(kwargs['rison']['name']) + ) + +Finally to properly handle all possible exceptions use the ``safe`` decorator, +that will catch all uncaught exceptions for you and return a proper error response. +You can enable or disable stack trace response using the +``FAB_API_SHOW_STACKTRACE`` configuration key:: + + from flask_appbuilder.api import BaseApi, expose, rison, safe + + ... + + @expose('/error') + @safe + def error(self): + raise Exception + +OpenAPI spec +------------ + +We can define an OpenAPI specification by using YAML on the docs section of our +methods:: + + @expose('/greeting') + def greeting(self): + """Send a greeting + --- + get: + responses: + 200: + description: Greet the user + content: + application/json: + schema: + type: object + properties: + message: + type: string + """ + return self.response(200, message="Hello") + + +We are defining that, our endpoint will respond to HTTP GET with a JSON object that contains +a key ``message`` with values of type **string**. To access all our OpenAPI specifications +request it on ``/api/v1/_openapi``, this is a dynamic endpoint that will serve all specs +from different API versions. So if we register an API for version **v2** we access it's +spec on ``/api/v2/_openapi``. Please note that OpenAPI specs are subject to authentication. + +So our spec for a method that accepts two HTTP verbs:: + + @expose('/greeting2', methods=['POST', 'GET']) + def greeting2(self): + """Send a greeting + --- + get: + responses: + 200: + description: Greet the user + content: + application/json: + schema: + type: object + properties: + message: + type: string + post: + responses: + 201: + description: Greet the user + content: + application/json: + schema: + type: object + properties: + message: + type: string + """ + if request.method == 'GET': + return self.response(200, message="Hello (GET)") + return self.response(201, message="Hello (POST)") + + +To access Swagger UI you must enable ``FAB_API_SWAGGER_UI = True`` on your config file +then goto ``http://localhost:8080/swaggerview/v1`` for OpenAPI **v1** definitions +On Swagger UI our example API looks like: + +.. image:: ./images/swagger001.png + :width: 70% + + +Notice the ``get`` and ``put`` structures, we should always detail all our +possible responses. The ``BaseApi`` class comes with some pre packaged HTTP +responses we can use for the sake of brevity:: + + @expose('/error') + @protect() + @safe + def error(self): + """Error 500 + --- + get: + responses: + 500: + $ref: '#/components/responses/500' + """ + raise Exception + +A complete list of packaged responses you can use:: + + responses: + 400: + $ref: '#/components/responses/400' + 401: + $ref: '#/components/responses/401' + 404: + $ref: '#/components/responses/404' + 422: + $ref: '#/components/responses/422' + 500: + $ref: '#/components/responses/500' + +The automatic OpenAPI spec generation also supports **Rison** arguments and their +json schema spec. Since both are compatible we can reuse our Json schema spec on OpenAPI. +First we need to register our spec, using ``apispec_parameter_schemas`` dictionary:: + + + class ExampleApi(BaseApi): + + resource_name = 'example' + apispec_parameter_schemas = { + "greeting_schema": greeting_schema + } + + +FAB will register your schema on ``/components/parameters``, so you can now +easily reference them:: + + @expose('/greeting4') + @rison(greeting_schema) + def greeting4(self, **kwargs): + """Get item from Model + --- + get: + parameters: + - $ref: '#/components/parameters/greeting_schema' + responses: + 200: + description: Greet the user + content: + application/json: + schema: + type: object + properties: + message: + type: string + """ + return self.response( + 200, + message="Hello {}".format(kwargs['rison']['name']) + ) + + +Security +-------- + +FAB offers user management, several authentication backends and granular role base access +so we can use these features on the API also. Default API authentication method is done +using JSON Web Tokens (JWT). + +:tip: + + FAB's JWT authentication is done with flask-jwt-extended. + Checkout it's documentation for custom configuration: + https://flask-jwt-extended.readthedocs.io/en/latest/options.html + +Next, let's see how to create a private method:: + + from flask import request + from flask_appbuilder.api import BaseApi, expose, rison + from flask_appbuilder.security.decorators import protect + from . import appbuilder + + + class ExampleApi(BaseApi): + + ... + @expose('/private') + @protect() + def rison_json(self): + """Say it's risonjson + --- + get: + responses: + 200: + description: Say it's private + content: + application/json: + schema: + type: object + 401: + $ref: '#/components/responses/401' + """ + return self.response(200, message="This is private") + + + appbuilder.add_api(ExampleApi) + +Accessing this method as expected will +return an HTTP 401 not authorized code and message:: + + $ curl -v 'http://localhost:8080/api/v1/example/private' + ... + < HTTP/1.0 401 UNAUTHORIZED + < Content-Type: application/json + ... + { + "msg": "Missing Authorization Header" + } + +So we need to first obtain our JSON Web token, for this, FAB registers a login endpoint. +For this we POST request with a JSON payload using:: + + { + "username": "", + "password": "", + "provider": "db|ldap" + } + +Notice the *provider* argument, FAB currently supports DB and LDAP +authentication backends for the Api. The login endpoint returns a fresh **access token** and optionally +a **refresh token**. You can renew the **access token** using the **refresh token** but this time +the returned token will not be fresh. To obtain a new non fresh access token +use ``refresh`` endpoint with the **refresh token**. To obtain a **refresh token** on the login endpoint +send the optional parameter **"refresh": true** on the JSON PUT payload. + +Let's request our Token then:: + + # If not already, create an admin user + $ fabmanager create-admin + Username [admin]: + User first name [admin]: + User last name [user]: + Email [admin@fab.org]: + Password: + Repeat for confirmation: + ... + Admin User admin created. + + # Login to obtain a token + $ curl -XPOST http://localhost:8080/api/v1/security/login -d \ + '{"username": "admin", "password": "password", "provider": "db"}' \ + -H "Content-Type: application/json" + { + "access_token": "" + } + # It's nice to use the Token as an env var + $ export TOKEN="" + +Next we can use our token on protected endpoints:: + + $ curl 'http://localhost:8080/api/v1/example/private' -H "Authorization: Bearer $TOKEN" + { + "message": "This is private" + } + +As always FAB created a new **can_private** permission +on the DB and as associated it to the *Admin* Role. +So the Admin role as a new permission on +a view named "can private on ExampleApi" +Note that you can protect all your methods and make +them public or not by adding them to the *Public* Role. + +Also to restrict the default permissions we can use ``base_permissions`` +list property. This can be specially useful on ``ModelRestApi`` (up next) +where we can restrict our Api resources to be read only, or only allow POST +methods:: + + class ExampleApi(BaseApi): + base_permissions = ['can_private'] + + +You can create an alternate JWT user loader, this can be useful if you want +to use an external Authentication provider and map the JWT identity to your +user Model:: + + @appbuilder.sm.jwt_manager.user_loader_callback_loader + def alternate_user_loader(identity): + # find the user by it's identity + ... + return user + +Optionally you can enable signed cookie sessions (from flask-login) on the +API. You can do it class or method wide:: + + class ExampleApi(BaseApi): + allow_browser_login = True + +The previous example will enable cookie sessions on the all class:: + + class ExampleApi(BaseApi): + + @expose('/private') + @protect(allow_browser_login=True) + def private(self) + .... + +On the previous example, we are enabling signed cookies on the ``private`` method. Not that event then +valid a valid JWT is also accepted. + +Model REST API +-------------- + +To automatically create a RESTfull CRUD Api from a database *Model*, use ``ModelRestApi`` class and +define it almost like an MVC ``ModelView``. This class will expose the following REST endpoints + +:note: + Follow this example on Flask-AppBuilder project ./examples/crud_rest_api/ + + .. cssclass:: table-bordered table-hover + ++-----------------------------+-------------------------------------------------------+-----------------+--------+ +| URL | Description | Permission Name | HTTP | ++=============================+=======================================================+=================+========+ +| /_info | Returns info about the CRUD model and security | can_info | GET | ++-----------------------------+-------------------------------------------------------+-----------------+--------+ +| / | Queries models data, receives args as Rison | can_get | GET | ++-----------------------------+-------------------------------------------------------+-----------------+--------+ +| / | Returns a single model from it's primary key (id) | can_get | GET | ++-----------------------------+-------------------------------------------------------+-----------------+--------+ +| / | Receives a JSON payload as POST and creates record | can_post | POST | ++-----------------------------+-------------------------------------------------------+-----------------+--------+ +| / | Receives a JSON payload as PUT and updates record | can_put | PUT | ++-----------------------------+-------------------------------------------------------+-----------------+--------+ +| / | Deletes a single model from it's primary key (id) | can_delete | DELETE | ++-----------------------------+-------------------------------------------------------+-----------------+--------+ + +For each ``ModelRestApi`` you will get 5 CRUD endpoints and an extra information method. +All created CRUD endpoints have their OpenAPI spec accessible on ``/api//_openapi``, +each class is tagged so the CRUD endpoints get nicely grouped when using Swagger UI. +Notice that ``ModelRestApi`` will generate a complete OpenAPI schema models for you data, +so you can get free documentation for you API's. +Let's dive into a simple example using the quickhowto. +The quickhowto example as a Contact's Model and a Group Model, +so each Contact belongs to a Group. + +First let's define a CRUD REST Api for our Group model resource:: + + from flask_appbuilder.models.sqla.interface import SQLAInterface + from flask_appbuilder.api import ModelRestApi + from . import appbuilder + + + class GroupModelApi(ModelRestApi): + resource_name = 'group' + datamodel = SQLAInterface(ContactGroup) + + appbuilder.add_api(GroupModelApi) + + +Behind the scenes FAB uses marshmallow-sqlalchemy to infer the Model to a Marshmallow Schema, +that can be safely serialized and deserialized. Let's recall our Model definition for ``ContactGroup``:: + + class ContactGroup(Model): + id = Column(Integer, primary_key=True) + name = Column(String(50), unique=True, nullable=False) + + def __repr__(self): + return self.name + +Swagger UI API representation for groups (http://localhost:8080/swaggerview/v1): + +.. image:: ./images/swagger002.png + :width: 70% + + +All endpoints are protected so we need to request a JWT and use it on our REST resource, +like shown before we need to make a PUT request to the login API endpoint:: + + # Login to obtain a token + $ curl -XPOST http://localhost:8080/api/v1/security/login -d \ + '{"username": "admin", "password": "password", "provider": "db"}' \ + -H "Content-Type: application/json" + { + "access_token": "" + } + # It's nice to use the Token as an env var + $ export TOKEN="" + +First let's create a Group:: + + $ curl -XPOST http://localhost:8080/api/v1/group/ -d \ + '{"name": "Friends"}' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "id": 1, + "result": { + "name": "Friends" + } + } + + +We got back a response with the model id and result with the inserted data. +Now let's query our newly created Group:: + + $ curl http://localhost:8080/api/v1/group/1 \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + + { + "description_columns": {}, + "show_title": "Show Contact Group", + "show_columns": [ + "name" + ], + "label_columns": { + "name": "Name" + }, + "id": "1", + "result": { + "name": "Friends" + } + } + +As you can see, the API returns the model data, and extra meta data so you can properly render +a page with labels, descriptions and defined column order. This way it should be possible +to develop a React component (for example) that renders any model just by switching between HTTP endpoints. +It's also possible to just ask for certain meta data keys, we will talk about this later. + +Next let's change our newly created model (HTTP PUT):: + + $ curl -XPUT http://localhost:8080/api/v1/group/1 -d \ + '{"name": "Friends Changed"}' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "result": { + "name": "Friends Changed" + } + } + +And finally test the delete method (HTTP DELETE):: + + $ curl -XDELETE http://localhost:8080/api/v1/group/1 \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "message": "OK" + } + +Let's check if it exists (HTTP GET):: + + $ curl http://localhost:8080/api/v1/group/1 \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "message": "Not found" + } + + +We get an HTTP 404 (Not found). + +Information endpoint +-------------------- + +This endpoint serves as a method to fetch meta information about our CRUD +methods. Again the main purpose to serve meta data is to make possible for a frontend +layer to be able to render dynamically: + +- Search options + +- Forms + +- Enable/disable features based on permissions. + +First a birds eye view from the output of the **_info** endpoint:: + + { + "add_columns": [...], + "edit_columns": [...], + "add_title": "...", + "edit_title": "...", + "filters": {...}, + "permissions": [...] + } + +Let's drill down this data structure, ``add_columns`` and ``edit_columns`` are similar +and serve to aid on rendering forms for add and edit so their response contains the +following data structure:: + + { + "add_columns": [ + { + "description": "", + "label": "", + "name": "", + "required": true|false, + "unique": true|false, + "type": "String|Integer|Related|RelatedList|...", + "validate": [ ... list of validation methods ... ] + "count": + "values" : [ ... optional with all possible values for a related field ... ] + }, + ... + ] + } + +Edit fields ``edit_columns`` is similar, but it's content may be different, since +we can configure it in a distinct way + +Next, filters, this returns all the necessary info to render all possible filters allowed +by the backend database for each field on the model:: + + { + "filters": { + "": [ + { + "name": "", + "operator": "" + }, + ... + ], + ... + } + } + +Note that the **operator** value can be used to filter our list queries, +more about this later. + +Finally the permissions, this declares all allowed permissions for the current user. +Remember that these can extend the automatic HTTP methods generated by ``ModelRestApi`` +by just defining new methods and protecting them with the ``protect`` decorator:: + + { + "permissions": ["can_get", "can_put", ... ] + } + +On all GET HTTP methods we can select which meta data keys we want, this can +be done using *Rison* URI arguments. So the **_info** endpoint is no exception. +The across the board way to filter meta data is to send a GET request +using the following structure:: + + { + "keys": [ ... LIST OF META DATA KEYS ... ] + } + +That translates to the following in *Rison* for fetching just the permissions meta data:: + + (keys:!(permissions)) + +So, back to our example:: + + $ curl 'http://localhost:8080/api/v1/group/_info?q=(keys:!(permissions))' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "permissions": [ + "can_get", + "can_post", + "can_put", + "can_delete" + ] + } + +And to fetch the permissions and Add form fields info:: + + $ curl 'http://localhost:8080/api/v1/group/_info?q=(keys:!(permissions,add_columns))' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "add_columns": [ ... ], + "permissions": [ + "can_get", + "can_post", + "can_put", + "can_delete" + ] + } + +To fetch meta data with internationalization use **_l_** URI key argument with i18n +country code as the value. This will work on any HTTP GET endpoint:: + + $ curl 'http://localhost:8080/api/v1/group/_info?q=(keys:!(permissions,add_columns))&_l_=pt' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "add_columns": [ ... ], + "permissions": [ + "can_get", + "can_post", + "can_put", + "can_delete" + ] + } + +Render meta data with *Portuguese*, labels, description, filters + +The ``add_columns`` and ``edit_columns`` keys also render all possible +values from related fields, using our *quickhowto* example:: + + { + "add_columns": [ + { + "description": "", + "label": "Gender", + "name": "gender", + "required": false, + "unique": false, + "type": "Related", + "count": 2, + "values": [ + { + "id": 1, + "value": "Male" + }, + { + "id": 2, + "value": "Female" + } + ] + }, + ... + ] + } + +These related field values can be filtered server side using the ``add_query_rel_fields`` +or ``edit_query_rel_fields``:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + add_query_rel_fields = { + 'gender': [['name', FilterStartsWith, 'F']] + } + +You can also impose an order for these values server side using ``order_rel_fields``:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + order_rel_fields = { + 'contact_group': ('name', 'asc'), + 'gender': ('name', 'asc') + } + +The previous example will filter out only the **Female** gender from our list +of possible values + +Note that these related fields may render a long list of values, so pagination +is available and subject to a max page size. You can paginate these values using +the following Rison argument structure:: + + { + "add_columns": { + : { + 'page': int, + 'page_size': int + } + } + } + +Using Rison example:: + + (add_columns:(contact_group:(page:0,page_size:10))) + + +We can also restrict server side the available fields for add and edit using ``add_columns`` +and ``edit_columns``. Additionally you can use ``add_exclude_columns`` and ``edit_exclude_columns``:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + add_columns = ['name'] + +Will only return the field *name* from our *Contact* model information endpoint for ``add_fields`` + +Get Item +-------- + +The get item endpoint is very simple, and was already covered to some extent. +The response data structure is:: + + { + "id": "" + "description_columnns": {}, + "label_columns": {}, + "show_columns": [], + "show_title": "", + "result": {} + } + +Now we are going to cover the *Rison* arguments for custom fetching +meta data keys or columns. This time the accepted arguments are slightly extended:: + + { + "keys": [ ... List of meta data keys to return ... ], + "columns": [ ... List of columns to return ... ] + } + +So for fetching only the *name* and *address* for a certain *Contact*, using *Rison*:: + + (columns:!(name,address)) + +Our *curl* command will look like:: + + curl 'http://localhost:8080/api/v1/contact/1?q=(columns:!(name,address))' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "description_columns": {}, + "id": "1", + "show_columns": [ + "name", + "address" + ], + "show_title": "Show Contact", + "label_columns": { + "address": "Address", + "name": "Name" + }, + "result": { + "address": "Street phoung", + "name": "Wilko Kamboh" + } + } + +And to only include the *label_columns* meta data, *Rison* data structure:: + + (columns:!(name,address),keys:!(label_columns)) + +Our *curl* command will look like:: + + curl 'http://localhost:8080/api/v1/contact/1?q=(columns:!(name,address),keys:!(label_columns))' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "id": "1", + "label_columns": { + "address": "Address", + "name": "Name" + }, + "result": { + "address": "Street phoung", + "name": "Wilko Kamboh" + } + } + +To discard completely all meta data use the special key ``none``:: + + (columns:!(name,address),keys:!(none)) + +Our *curl* command will look like:: + + curl 'http://localhost:8080/api/v1/contact/1?q=(columns:!(name,address),keys:!(none))' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "id": "1", + "result": { + "address": "Street phoung", + "name": "Wilko Kamboh" + } + } + + +We can restrict or add fields for the get item endpoint using +the ``show_columns`` property. This takes precedence from the *Rison* arguments:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + show_columns = ['name'] + +We can add fields that are python functions also, for this on the SQLAlchemy definition, +let's add a new function:: + + class Contact(Model): + id = Column(Integer, primary_key=True) + name = Column(String(150), unique=True, nullable=False) + address = Column(String(564)) + birthday = Column(Date, nullable=True) + personal_phone = Column(String(20)) + personal_celphone = Column(String(20)) + contact_group_id = Column(Integer, ForeignKey('contact_group.id'), nullable=False) + contact_group = relationship("ContactGroup") + gender_id = Column(Integer, ForeignKey('gender.id'), nullable=False) + gender = relationship("Gender") + + def __repr__(self): + return self.name + + def some_function(self): + return "Hello {}".format(self.name) + +And then on the REST API:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + show_columns = ['name', 'some_function'] + +The ``show_columns`` is also useful to impose an order on the columns. +Again this is useful to develop a dynamic frontend show item page/component +by using the *include_columns* meta data key. + +Note that this can be done on the query list endpoint also using ``list_columns`` + +Lists and Queries +----------------- + +Finally for our last HTTP endpoint, and the most feature rich. +The response data structure is:: + + { + "count": + "ids": [ ... List of PK's ordered by result ... ], + "description_columns": {}, + "label_columns": {}, + "list_columns": [ ... An ordered list of columns ...], + "order_columns": [ ... List of columns that can be ordered ... ], + "list_title": "", + "result": {} + } + +As before meta data can be chosen using *Rison* arguments:: + + (keys:!(label_columns)) + +Will only fetch the *label_columns* meta data key + +And we can choose which columns to fetch:: + + (columns:!(name,address)) + +To reduce or extend the default inferred columns from our *Model*. +On server side we can use the ``list_columns`` property, +this takes precedence over *Rison* arguments:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + list_columns = ['name', 'address'] + +FAB supports dotted notation (one level on GET methods only) so you can control what columns get +rendered on related nested columns this applies with order by fields:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + list_columns = ['name', 'address', 'contact_group.name'] + +By default related columns on this case ``contact_group`` will create a nested +complete sub schema (on our example will return {"contact_group": {"name", "id"}}. + +For ordering the results, the following will order contacts by name descending Z..A:: + + (order_column:name,order_direction:desc) + +To set a default order server side use ``base_order`` tuple:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + base_order = ('name', 'desc') + +Pagination, get the second page using page size of two (just an example):: + + (page:2,page_size:2) + +To set the default page size server side:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + page_size = 20 + +And last, but not least, *filters*. The query *filters* data structure:: + + { + "filters": [ + { + "col": , + "opr": , + "value": + }, + ... + ] + } + +All filters are **AND** operations. We can filter by several column names +using different operations, so using *Rison*:: + + (filters:!((col:name,opr:sw,value:a),(col:name,opr:ew,value:z))) + +The previous filter will query all contacts whose **name** starts with "a" and ends with "z". +The possible operations for each field can be obtained from the information endpoint. +FAB can filter your models by any field type and all possible operations + +Note that all *Rison* arguments can be used alone or in combination:: + + (filters:!((col:name,opr:sw,value:a),(col:name,opr:ew,value:z)),columns:!(name),order_columns:name,order_direction:desc) + +Will filter all contacts whose **name** starts with "a" and ends with "z", using descending name order by, and +just fetching the **name** column. + +To impose base filters server side:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + base_filters = [['name', FilterStartsWith, 'A']] + +The filter will act on all HTTP endpoints, protecting delete, create, update and display +operations + +Simple example using doted notation, FAB will infer the necessary join operation:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + base_filters = [['contact_group.name', FilterStartsWith, 'F']] + +Locks all contacts, to groups whose name starts with "F". Using the provided test data +on the quickhowto example, limits the contacts to family and friends. + +Updates and Partial Updates +--------------------------- + +PUT methods allow for changing a **Model**. Allowed changes are controlled by +``edit_columns``:: + + class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + edit_columns = ['name'] + +First let's create a new contact:: + + curl -XPOST 'http://localhost:8080/api/v1/contact/' -H "Authorization: Bearer $TOKEN" -d \ + '{"name":"New Contact", "personal_celphone":"1234", "contact_group": 1, "gender":1}' \ + -H "Content-Type: application/json" + { + "id": 4, + "result": { + "address": null, + "birthday": null, + "contact_group": 1, + "gender": 1, + "name": "New Contact", + "personal_celphone": "1234", + "personal_phone": null + } + } + +So if you submit a change for ``personal_celphone``:: + + $ curl -v XPUT http://localhost:8080/api/v1/contact/4 -d \ + '{"name": "Change name", "personal_celphone": "this should not change"}' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "result": { + "name": "Change name" + } + } + +Let's confirm:: + + curl -XGET 'http://localhost:8080/api/v1/contact/4' -H "Authorization: Bearer $TOKEN" + { + .... + "id": "4", + "result": { + "address": null, + "birthday": null, + "contact_group": { + "id": 1, + "name": "Friends" + }, + "gender": { + "id": 1, + "name": "Male" + } + "name": "Change name", + "personal_celphone": "1234", + "personal_phone": null + } + } + +The PUT method may also work like a PATCH method, remove the ``edit_columns`` from the API class +and test a partial update:: + + $ curl -v XPUT http://localhost:8080/api/v1/contact/ -d \ + '{"personal_celphone": "4321"}' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "result": { + "address": null, + "birthday": null, + "contact_group": 1 + "gender": 1, + "name": "Change name", + "personal_celphone": "4321", + "personal_phone": null + } + } + + + +Validation and Custom Validation +-------------------------------- + +Notice that by using marshmallow with SQLAlchemy, +we are validating field size, type and required fields out of the box. +This is done by marshmallow-sqlalchemy that automatically creates ModelSchema's +inferred from our SQLAlchemy Models. +But you can always use your own defined Marshmallow schemas independently +for add, edit, list and show endpoints. + +A validation error for PUT and POST methods returns HTTP 422 and the following JSON data:: + + { + "message": { + "": [ + "", + ... + ], + ... + } + } + +Next we will test some basic validation, first the field type +by sending a name that is a number:: + + $ curl XPOST http://localhost:8080/api/v1/group/ -d \ + '{"name": 1234}' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "message": { + "name": [ + "Not a valid string." + ] + } + } + +And we get an HTTP 422 (Unprocessable Entity). + +How to add custom validation? On our next example we only allow +group names that start with a capital "A":: + + from marshmallow import Schema, fields, ValidationError, post_load + + + def validate_name(n): + if n[0] != 'A': + raise ValidationError('Name must start with an A') + + class GroupCustomSchema(Schema): + name = fields.Str(validate=validate_name) + + @post_load + def process(self, data): + return ContactGroup(**data) + +Then on our Api class:: + + class GroupModelRestApi(ModelRestApi): + resource_name = 'group' + add_model_schema = GroupCustomSchema() + edit_model_schema = GroupCustomSchema() + datamodel = SQLAInterface(ContactGroup) + +Let's try it out:: + + $ curl -v XPOST http://localhost:8080/api/v1/group/ -d \ + '{"name": "BOLA"}' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN" + { + "message": { + "name": [ + "Name must start with an A" + ] + } + } + +Overriding completely the marshmallow Schema gives you complete control +but can become very cumbersome for **Models** with many attributes, there is +a simpler way of doing this using ``validators_columns`` property:: + + class GroupModelRestApi(ModelRestApi): + resource_name = 'group' + datamodel = SQLAInterface(ContactGroup) + validators_columns = {'name': validate_name} + + +Pre and Post processing +----------------------- + +``ModelRestApi`` offers several methods that you can override to perform pre processing or post processing +on all HTTP methods. These methods are nice places to change data before submission or retrieval: + +.. automodule:: flask_appbuilder.api + + .. autoclass:: ModelRestApi + :members: pre_get, pre_get_list, pre_update, post_update, pre_add, post_add, pre_delete, post_delete + :noindex: + +Enum Fields +----------- + +``ModelRestApi`` offers support for **Enum** fields, you have to declare them +on a specific way:: + + class GenderEnum(enum.Enum): + male = 'Male' + female = 'Female' + + + class Contact(Model): + id = Column(Integer, primary_key=True) + name = Column(String(150), unique=True, nullable=False) + address = Column(String(564)) + birthday = Column(Date, nullable=True) + personal_phone = Column(String(20)) + personal_celphone = Column(String(20)) + contact_group_id = Column(Integer, ForeignKey('contact_group.id'), nullable=False) + contact_group = relationship("ContactGroup") + gender = Column(Enum(GenderEnum), nullable=False, info={"enum_class": GenderEnum}) + +Notice the ``info={"enum_class": GenderEnum}`` diff --git a/examples/base_api/README.rst b/examples/base_api/README.rst new file mode 100644 index 000000000..91960689d --- /dev/null +++ b/examples/base_api/README.rst @@ -0,0 +1,10 @@ +Base Api example +---------------- + +Simple example showing how to use *BaseApi* class + +Run it:: + + $ fabmanager run + + diff --git a/examples/base_api/app/__init__.py b/examples/base_api/app/__init__.py new file mode 100644 index 000000000..c9dd15b7e --- /dev/null +++ b/examples/base_api/app/__init__.py @@ -0,0 +1,19 @@ +import logging +from flask import Flask +from flask_appbuilder import SQLA, AppBuilder + +""" + Logging configuration +""" +logging.basicConfig(format='%(asctime)s:%(levelname)s:%(name)s:%(message)s') +logging.getLogger().setLevel(logging.DEBUG) + + +app = Flask(__name__) +app.config.from_object('config') +db = SQLA(app) +appbuilder = AppBuilder(app, db.session) + + +from . import api + diff --git a/examples/base_api/app/api.py b/examples/base_api/app/api.py new file mode 100644 index 000000000..99bc07ca3 --- /dev/null +++ b/examples/base_api/app/api.py @@ -0,0 +1,154 @@ +from flask import request +from flask_appbuilder.api import BaseApi, expose, rison, safe +from flask_appbuilder.security.decorators import protect +from . import appbuilder + +greeting_schema = { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } +} + + +class ExampleApi(BaseApi): + + resource_name = 'example' + apispec_parameter_schemas = { + "greeting_schema": greeting_schema + } + + @expose('/greeting') + def greeting(self): + """Send a greeting + --- + get: + responses: + 200: + description: Greet the user + content: + application/json: + schema: + type: object + properties: + message: + type: string + """ + return self.response(200, message="Hello") + + @expose('/greeting2', methods=['POST', 'GET']) + def greeting2(self): + """Send a greeting + --- + get: + responses: + 200: + description: Greet the user + content: + application/json: + schema: + type: object + properties: + message: + type: string + post: + responses: + 201: + description: Greet the user + content: + application/json: + schema: + type: object + properties: + message: + type: string + """ + if request.method == 'GET': + return self.response(200, message="Hello (GET)") + return self.response(201, message="Hello (POST)") + + @expose('/greeting3') + @rison() + def greeting3(self, **kwargs): + if 'name' in kwargs['rison']: + return self.response( + 200, + message="Hello {}".format(kwargs['rison']['name']) + ) + return self.response_400(message="Please send your name") + + @expose('/greeting4') + @rison(greeting_schema) + def greeting4(self, **kwargs): + """Get item from Model + --- + get: + parameters: + - $ref: '#/components/parameters/greeting_schema' + responses: + 200: + description: Greet the user + content: + application/json: + schema: + type: object + properties: + message: + type: string + """ + return self.response( + 200, + message="Hello {}".format(kwargs['rison']['name']) + ) + + @expose('/risonjson') + @rison() + def rison_json(self, **kwargs): + """Say it's risonjson + --- + get: + responses: + 200: + description: Say it's private + content: + application/json: + schema: + type: object + """ + return self.response(200, result=kwargs['rison']) + + @expose('/private') + @protect() + def private(self): + """Say it's private + --- + get: + responses: + 200: + description: Say it's private + content: + application/json: + schema: + type: object + 401: + $ref: '#/components/responses/401' + """ + return self.response(200, message="This is private") + + @expose('/error') + @protect() + @safe + def error(self): + """Error 500 + --- + get: + responses: + 500: + $ref: '#/components/responses/500' + """ + raise Exception + + +appbuilder.add_api(ExampleApi) diff --git a/examples/base_api/app/models.py b/examples/base_api/app/models.py new file mode 100644 index 000000000..453bc2717 --- /dev/null +++ b/examples/base_api/app/models.py @@ -0,0 +1,11 @@ +from flask_appbuilder import Model + +""" + +You can use the extra Flask-AppBuilder fields and Mixin's + +AuditMixin will add automatic timestamp of created and modified by who + + +""" + diff --git a/examples/base_api/config.py b/examples/base_api/config.py new file mode 100644 index 000000000..3a027b7e0 --- /dev/null +++ b/examples/base_api/config.py @@ -0,0 +1,58 @@ +import os +basedir = os.path.abspath(os.path.dirname(__file__)) + +CSRF_ENABLED = True +SECRET_KEY = '\2\1thisismyscretkey\1\2\e\y\y\h' + +OPENID_PROVIDERS = [ + { 'name': 'Google', 'url': 'https://www.google.com/accounts/o8/id' }, + { 'name': 'Yahoo', 'url': 'https://me.yahoo.com' }, + { 'name': 'AOL', 'url': 'http://openid.aol.com/' }, + { 'name': 'Flickr', 'url': 'http://www.flickr.com/' }, + { 'name': 'MyOpenID', 'url': 'https://www.myopenid.com' }] + +SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db') +#SQLALCHEMY_DATABASE_URI = 'mysql://myapp@localhost/myapp' +#SQLALCHEMY_DATABASE_URI = 'postgresql://root:password@localhost/myapp' +BABEL_DEFAULT_LOCALE = 'en' + + +#------------------------------ +# GLOBALS FOR APP Builder +#------------------------------ +FAB_API_SWAGGER_UI = True + +BABEL_DEFAULT_LOCALE = 'en' +BABEL_DEFAULT_FOLDER = 'translations' +LANGUAGES = { + 'en': {'flag':'gb', 'name':'English'}, + 'pt': {'flag':'pt', 'name':'Portuguese'}, + 'es': {'flag':'es', 'name':'Spanish'}, + 'de': {'flag':'de', 'name':'German'}, + 'zh': {'flag':'cn', 'name':'Chinese'}, + 'ru': {'flag':'ru', 'name':'Russian'} +} + + +UPLOAD_FOLDER = basedir + '/app/static/uploads/' +IMG_UPLOAD_FOLDER = basedir + '/app/static/uploads/' +IMG_UPLOAD_URL = '/static/uploads/' +AUTH_TYPE = 1 +AUTH_ROLE_ADMIN = 'Admin' +AUTH_ROLE_PUBLIC = 'Public' +#APP_NAME = "My App Name" +#APP_ICON = "static/img/logo.jpg" +APP_THEME = "" # default +#APP_THEME = "cerulean.css" +#APP_THEME = "amelia.css" +#APP_THEME = "cosmo.css" +#APP_THEME = "cyborg.css" +#APP_THEME = "flatly.css" +#APP_THEME = "journal.css" +#APP_THEME = "readable.css" +#APP_THEME = "simplex.css" +#APP_THEME = "slate.css" +#APP_THEME = "spacelab.css" +#APP_THEME = "united.css" +#APP_THEME = "yeti.css" + diff --git a/examples/base_api/run.py b/examples/base_api/run.py new file mode 100644 index 000000000..85b107462 --- /dev/null +++ b/examples/base_api/run.py @@ -0,0 +1,3 @@ +from app import app + +app.run(host='0.0.0.0', port=8080, debug=True) diff --git a/examples/crud_rest_api/NAMES.DIC b/examples/crud_rest_api/NAMES.DIC new file mode 100644 index 000000000..b7266ad64 --- /dev/null +++ b/examples/crud_rest_api/NAMES.DIC @@ -0,0 +1,27607 @@ +aaccf +aalders +aaren +aarika +aaron +aartjan +aasen +ab +abacus +abadines +abagael +abagail +abahri +abasolo +abazari +abba +abbai +abbas +abbatant +abbate +abbe +abbey +abbi +abbie +abbot +abbott +abby +abbye +abdalla +abdallah +abdel +abdel-az +abdel-ma +abdel-ra +abdel-sa +abdelazi +abdelmad +abdelrah +abdelran +abdelsal +abderrao +abderraz +abdi +abdo +abdollah +abdolrah +abdou +abdrani +abdul +abdul-az +abdul-ma +abdul-no +abdul-ra +abdul-sa +abdulazi +abdulla +abdullah +abdulmad +abdulrah +abdulran +abdulsal +abdur +abe +abedi +abel +abelard +abell +abella +abellera +abello +abelow +abernath +aberneth +abeu +abey +abhay +abhijit +abi-aad +abid +abie +abigael +abigail +abigale +abike +abner +abou-arr +abou-ezz +aboul-ma +aboussou +abovyan +abra +abraham +abrahan +abrahim +abram +abramo +abrams +abran +abrar +absi +abu +abul +access +accounti +acelvari +achal +achamma +acharyya +achcar +achille +achkar +achmad +ackaouy +acker +acklin +ackwood +acree +acres +acs +action +actionte +acton +aczel +ad +ada +adah +adahm +adair +adal +adaline +adam +adamczyk +adamkows +adamo +adamowic +adams +adamski +adamson +adamyk +adan +adara +adcock +adcox +adda +addetia +addi +addia +addie +addison +addona +addons +addy +ade +adebayo +adel +adela +adelaida +adelaide +adelbert +adele +adelheid +adelia +adelice +adelina +adelind +adeline +adella +adelle +adena +adeney +adeniyi +aderhold +adey +adham +adhem +adi +adiana +adib +adie +adil +adimari +adina +aditya +adjangba +adkinson +adlai +adler +adlin +admad +admin +admin-mt +administ +adnan +adnane +ado +adolf +adolfie +adolph +adolphe +adolpho +adolphus +adora +adore +adoree +adornato +adorne +adorno +adrea +adri +adria +adriaan +adriaans +adriaens +adrian +adriana +adriane +adrianna +adrianne +adriano +adrie +adrien +adriena +adriene +adrienne +adrion +advance +ae +aeinstei +aeriel +aeriela +aeriell +aery +afaq +afif +afke +afkham +afkham-e +afo +afton +afzal +ag +agace +agam +agarwal +agata +agatha +agathe +agen +agenia +aggarwal +aggi +aggie +aggregat +aggy +aghi +aghili +agily +agna +agnar +agnella +agnes +agnese +agnesse +agneta +agnew +agnihotr +agnola +agostino +agosto +agretha +aguiar +aguie +aguilar +aguinsky +aguirre +aguistin +aguste +agustin +ahad +aharon +ahbeng +ahdieh +ahlberg +ahlers +ahluwali +ahmad +ahmadi +ahmed +ahmet +ahn +ai-mei +ai-tsung +aida +aidan +aidarous +aideen +aiden +aigneis +aihua +aija +aiken +aila +ailbert +aile +ailee +aileen +ailene +ailey +aili +ailina +ailis +ailsun +ailyn +aiman +aime +aimee +aimei +aimil +aimone +aindrea +ainslee +ainsley +ainslie +ainswort +air +aisha +aitken +aitsung +ajay +ajersch +ajeya +ajit +ajmal +ajoy +akai +akbar +akbas +akemi +akens +akers +akhavan +akhil +akhtar +akihiko +akim +akin +akinniyi +akio +akira +akita +akkerman +akram +akrawi +aksel +akshay +akyurekl +al +al bud +al-basi +al-tarab +aladanga +aladin +alain +alaine +alair +alameda +alan +alana +alanah +aland +alane +alanis +alanna +alano +alanoly +alanson +alanturi +alard +alaric +alary +alasdair +alastair +alasteir +alaster +alavi +alayne +alb +alba +albea +albeon +alber +alberik +albers +alberse +albert +alberta +albertei +albertin +alberto +alberts +alberty +albery +albie +albina +albiston +albrecht +albright +albritto +albtenta +alburger +alcantar +alcindor +alcock +alcott +alden +alderdic +aldhizer +aldin +aldis +aldo +aldon +aldous +aldric +aldrich +aldridge +aldus +aldwin +aldyn +alec +alecia +aleda +aleece +aleen +alegre +aleinste +alejandr +alejoa +aleke +aleksand +aleksic +alena +alene +aleong +alese +alessand +aleta +alethea +alev +alex +alexa +alexan +alexande +alexandr +alexei +alexi +alexia +alexina +alexine +alexio +alexis +alexon +alexson +alf +alfaro +alfi +alfie +alfons +alfonse +alfonso +alfonzo +alford +alfred +alfreda +alfredo +alfy +algernon +algie +algimant +algood +alguire +ali +alia +alic +alica +alice +alicea +alicia +alick +alida +alidia +alidina +alie +alika +alikee +alikhan +alina +aline +alink +alioto +alireza +alis +alisa +alisande +alisha +alison +alissa +alistair +alister +alisun +alix +aliza +alka +alkarim +alkire +all the +alla +allahdin +allahyar +allam +allaman +allan +allard +allaway +allaye-c +allayne +alleen +allegra +allen +allene +alles +alleva +alley +alleyn +alleyne +allgood +alli +allianor +allida +allie +allin +allina +allis +allisan +allison +allissa +allister +allistir +allix +allman +allsun +allwork +allx +ally +allyce +allyn +allys +allyson +alma +almeda +almeddah +almena +almendar +almeria +almerind +almeta +almira +almire +almon +alms +alnoor +aloi +aloin +aloise +aloisia +alok +alomari +alon +alonso +alonzo +alora +aloysia +aloysius +alp +alparsla +alperovi +alphard +alphen +alphonse +alphonso +alpine +alred +alric +alsaleh +alshabou +alsop +alspaugh +alstine +alston +alswiti +alta +altadonn +altay +alteen +altekar +alten +alternat +althea +altherr +alting-m +altman +altmann +alturing +aluin +aluino +alva +alvan +alvarez +alvaro +alvean +alvera +alverta +alvi +alvie +alvin +alvina +alvinia +alvino +alvira +alvis +alvy +alwin +alwyn +aly +alyce +alyda +alynn +alyosha +alyre +alys +alysa +alyse +alysia +alyson +alyss +alyssa +alzofon +amabel +amabelle +amadeus +amalea +amalee +amaleta +amalia +amalie +amalita +amalle +amalu +amand +amanda +amandi +amandie +amandip +amando +amandy +amant +amar +amara +amarendr +amargo +amarjit +amarsi +amarth +amata +amato +amavisca +ambach +amber +amberly +amble +ambler +ambroise +ambros +ambrose +ambrosi +ambrosio +ambrosiu +ambur +amby +amda +ame +amedeo +ameen +amelia +amelie +amelina +ameline +amelita +amelkar +amenta +america +amerigo +amery +amgad +ami +amick +amie +amigo +amii +amil +amin +amina +amini +aminuddi +aminzade +amiot +amir +amit +amitabh +amitava +amitie +amity +amiy +amjad +amlani +ammamari +ammar +ammiel +amnish +amnon +amol +amorim +amory +amos +amott +amour +amouzgar +amparo +amr +amrik +amril +amrish +amstutz +amu +amundsen +amy +amye +an +an-bin +an-son +ana +anabal +anabel +anabella +anabelle +anader +analiese +analise +anallese +anallise +anamary +anand +ananda +anandaro +ananmala +anant +ananth +anantha +ananyo +anar +anastasi +anastass +anatol +anatola +anatole +anatoli +anatollo +anatoly +anaya +anbin +ancel +ancell +anchia +anconeta +anctil +anda +andaree +andee +andeee +ander +anderea +anderer +anders +andersen +anderson +anderton +andi +andie +andiyono +andonis +andra +andrade +andras +andrassy +andre +andrea +andreana +andreas +andrease +andreato +andree +andrei +andrej +andrejs +andres +andress +andrew +andrews +andrey +andria +andriana +andric +andries +andriett +andris +andromac +andros +andrukat +andrus +andrusia +andruzzi +andrzej +andy +aneeta +aneko +anestass +anet +anett +anetta +anette +ange +angel +angela +angelako +angele +angeles +angeli +angelia +angelica +angelico +angelie +angeliek +angelika +angelina +angeline +angeliqu +angelita +angell +angelle +angelo +angerer +angermey +angie +angil +anglin +angobald +angus +angustia +angvall +angy +anh +anhorn +anhtuan +ania +anibal +anica +anika +aniko +anil +anila +anindita +anirban +anissa +anita +anitra +aniya +anja +anjali +anjanett +anje +anjela +anjli +anke +anker +anki +ankie +ankur +anky +ann +ann-hoon +ann-lorr +ann-mari +anna +anna-dia +anna-mar +annab +annabal +annabel +annabela +annabell +annable +annadian +annalea +annalee +annalies +annalisa +annalise +annamari +annamay +annarbor +annard +annas +anne +anne mar +anne-cor +anne-lis +anne-mar +annecori +anneke +annelies +annelise +annemari +annemie +annet +annetta +annette +anni +annibale +annice +annick +annie +annika +annis +annise +annissa +annmaria +annmarie +annnora +annora +annunzia +anny +anolik +anoop +anouk +anoushir +ans +ansar +ansel +ansell +anselm +anselma +anshel +ansley +anson +ansorger +anstead +anstett +anstice +ansys +antai +antanas +antanina +anthe +anthea +anthia +anthiath +anthonis +anthony +antin +antinucc +antkowia +antle +antoft +antoine +antoinet +anton +anton-ph +antonare +antone +antonell +antonett +antoni +antonia +antonie +antoniet +antonin +antonina +antonino +antonio +antonios +antonius +antons +antony +antti +antuan +antworth +anu +anup +anupam +anurag +anver +anvradha +anwar +any +anya +anzarout +anzures +aoki +aparicio +aparna +aphrodit +api-ecm +apiruksa +apollo +apostolo +appell +appenzel +applebau +applegar +appleton +appleyar +applicat +applicon +appoloni +appuglie +apriel +april +aprilett +aprill +apryle +apter +apurba +apurve +ara +arabadji +arabel +arabela +arabele +arabella +arabelle +aragon +aragorn +arai +araldo +aramideh +arana +arash +aravamud +arbel +arbenz +arbo +arbuckle +arch +archaimb +archamba +archana +archer +archibal +archibol +archie +archy +arco +arcouet +ard +arda +ardath +ardavan +ardeen +ardelia +ardelis +ardella +ardelle +arden +ardene +ardenia +ardie +ardiel +ardine +ardis +ardisj +ardith +ardizone +ardoin +ardra +ardyce +ardys +ardyth +aref +areg +arel +arellano +arend +arens +ares +aretha +areu +argento +argyriou +ari +ariadne +ariana +arias +aribindi +aric +aridatha +arie +ariel +ariela +ariella +arielle +arif +arin +arina +arine +ario +aris +aristide +aristotl +arjun +arkady +arkestei +arko +arlan +arlana +arlee +arleen +arlen +arlena +arlene +arles +arleta +arlette +arley +arleyne +arlie +arliene +arlin +arlina +arlinda +arline +arluene +arly +arlyn +arlyne +arman +armand +armande +armando +armbrust +armelia +armelle +armenaki +armenta +armentro +armes +armida +armijo +armin +armine +armitage +armolavi +armour +armstead +armstron +arn +arnaldo +arnauld +arnav +arne +arnett +arney +arni +arnie +arnis +arno +arnold +arnoldo +arnon +arnone +arnott +arnuad +arny +aroldo +aron +arona +aronovic +aronson +aronstam +arora +arpin +arpita +arrgh +arri +arro +arron +arsavir +arsena +arsenaul +arsene +arseneau +arshad +art +artair +arte +artemas +artemis +artemus +arther +arthur +artie +artiller +arto +artola +arts +artspssa +artur +arturo +artus +artuso +arty +artzer +arul +arumugam +arun +aruna +arunacha +arv +arvid +arvie +arvin +arvind +arvy +arwakhi +arya +aryavong +aryn +arzu +asa +asad +asan +asawa +asbill +asbjorn +asce +ascott +asdel +ase +asfazado +asghar +asgharza +ash +ashalata +ashar +ashbee +ashberry +ashbey +ashby +ashdown +ashely +asher +ashfaq +ashford +ashia +ashien +ashil +ashima +ashis +ashla +ashlan +ashlee +ashleigh +ashlen +ashley +ashli +ashlie +ashlin +ashly +ashmore +ashok +ashoka +ashrae +ashraf +ashruf +ashton +ashu +ashurkof +ashutosh +ashwin +ashwood- +ashworth +asia +asif +asing +asistore +askins +askold +asmar +asme +asnat +asops +asprer +asquin +assaad +assaf +asselin +assenza +assistan +associat +astaire +astalos +astle +astley +aston +astor +astorino +astra +astrid +astrix +aswini +atalanta +atalla +atcheson +atchison +atef +athalie +athanasi +athanass +athar +athena +athene +athony +athwal +atindra +atique +atkins +atkinson +atl +atl-sale +atlanta +atlante +atlantic +atmane +atoui +atp +atpco +atprs +atputhar +atrc +atsuo +atsushi +atta +attaie +attanasi +attarchi +attard +attaway +atte +attenbor +atteridg +attfield +attilio +attilla +atul +atwater +atwell +atwell-b +au +au-yang +au-yeung +aube +auberon +aubert +auberta +aubin +aubine +aubree +aubrette +aubrey +aubrie +aubry +aubuchon +aucoin +aud +audet +audette +audi +audie +audivox +audra +audre +audrean +audrey +audrie +audrienn +audry +audrye +audy +auerbach +augeri +augie +august +augusta +auguste +augustin +augusto +augustus +augy +auker +aula +aulakh +auld +ault +aumoine +aundrea +aunon +aura +aurea +aurel +aurelea +aurelia +aurelie +aurelius +auria +aurie +aurilia +auriol +aurlie +auro +auroora +aurora +aurore +aurthur +ausley +austen +austin +austina +austine +australi +auth +auto +auton +autoquot +auyeung +ava +avard +avaz +ave +avedis +aveline +avellane +averardo +averell +averett +averette +averil +averill +aversa +avery +averyl +avictor +avie +avigdor +avilez +avinash +avirett +avis +aviva +avivah +avra +avram +avril +avrit +avrom +avtar +awadalla +awadia +awan +awano +ax +axberg +axe +axel +ayako +ayandeh +ayao +ayaz +aybars +ayda +aydin +ayers +ayles +aylmar +aylmer +aylwin +aymer +ayn +ayodele +ayotte +ayoubzad +ayoup +ayrault +ayre +ayres +ayscue +ayse +ayukawa +aywie +ayyuce +azad +azam +azar +azari +azarshah +azer +azevedo +azhar +azim +aziz +azizuddi +azmak +azmeena +azmina +azra +azuma +azzuolo +ba +baab +baader +bab +baba +bababunm +babak +babalola +babar +babara +babasaki +babatund +babb +babbage +babbette +babbie +babcock +baber +babette +babin +babineau +babione +babita +babs +babu +baby +babyak +baccari +bacchioc +bacchus +bacciagl +bach +bachecon +bachelu +bachewic +bachitta +bachmann +bachner +bachynsk +backshal +bacon +baddeley +badelt +badenoch +badger +badjari +badmingt +badowski +badri +badza +bae +baenzige +baer +baerg +baets +bagetako +bagg +baggerma +baghdadi +bagi +bagnato +bagshaw +bagwell +baha +bahaa +bahadir +baheya +bahgat +bahia +bahl +bahman +bahoric +bahram +bail +bailetti +bailey +bailie +baillarg +baillie +bailloux +baily +bain +bainer +baines +bains +bainton +baird +bajada +bajpeyi +bakay +bakel +baker +baker-gr +bakhach +bakkum +bakoury +bal +balaban +balabani +balachan +balaji +balakris +balanger +balascak +balasing +balbir +balcom +bald +balderst +baldev +baldock +baldridg +balduin +baldwin +bale +bales +balfour +balgalvi +baljinde +balkenho +balkisso +ballanti +ballard +ballarte +ballinge +ballios +ballou +ballyk +balmer +balog +balogh +balraj +balsas +balser +balter +baltodan +balutis +balvinde +balwinde +bam +bambach +bambang +bambi +bambie +bamby +bame +bamfo +ban +banaei +bancroft +bandel +banens +banerd +banerjee +banez +banfalvi +bang +bangert +bangia +banh +banigan +banik +bank +bankhead +banks +banky +bannai +bannan +bannard +banniste +bansal +banu +banville +bao +baominh +baquero +bar +barabash +baragar +barakat +baran +barb +barba +barbabas +barbabra +barbara +barbara- +barbaraa +barbary +barbe +barbeau +barbee +barberen +barbette +barbey +barbi +barbie +barbour +barbra +barby +barclay +barcza +bard +barde +bardsley +bareham +barel +barenie +barentse +barfield +barham +bari +baribeau +baril +baris +barker +barkhous +barkley +barkwill +barlas +barlow +barn +barnabas +barnabe +barnaby +barnard +barnebas +barnes +barnett +barney +barnhard +barnhart +barnhill +barnhous +barnicke +barnie +barnwell +barny +barolet +baron +barr +barraclo +barrass +barrell +barret +barrett +barreyre +barri +barrie +barrient +barriere +barringt +barrio +barris +barritt +barron +barrows +barry +barsch +barsha +barsky +barsony +barstow +barszcze +bart +bartel +barth +barthel +barthole +bartholo +bartie +bartkows +bartlet +bartlett +bartley +bartolem +bartolom +bartoluc +barton +bartosze +bartra +bartram +bartush +barty +bartz +baruk +barwikow +bary +baryram +basa +basco +bascombe +base +basheer +bashton +bashyam +basia +basil +basile +basilio +basilius +basinger +baskaran +baskervi +baskin +basladyn +basmadji +basnett +bason +basrur +bassam +bassem +basser +bassett +bassigna +bassil +basta +bastani +bastarac +bastian +bastien +basu +bat +batcheld +batchelo +batchoun +bateman +bates +batholom +bathrick +bathsheb +batsheva +batson +battersb +battersh +battisto +batura +baudais +baudoin +bauer +baughan +baugnon +baulch +baum +baumann +baumberg +baumert +bautista +bawek +bawn +bax +baxie +baxter +baxy +bay +bayard +bayer +bayerkoh +bayless +bayley +bayly +bayne +baynes +bayno +bayola +bayrakta +bays +bazarjan +bazemore +bazerghi +bazerman +bazik +baziuk +bcs +bcspatch +bea +beach +beadley +beagley +beal +beale +beall +bealle +beals +beana +bear +beardmor +bearnard +bears +beasley +beata +beate +beato +beaton +beatrice +beatrisa +beatrix +beatriz +beattie +beattie- +beatty +beaty +beau +beaubien +beaucair +beauchai +beaucham +beauchem +beaudet +beaudett +beaudin +beaudoin +beaudry +beaufort +beaule +beaulieu +beaumier +beaumont +beaupre +beaurega +beausejo +beauvais +beavingt +beavis +beb +bebber +bebe +bebee +becan +becca +bechara +bechtel +beck +becka +becke +becker +beckett +beckham +becki +beckie +beckman +beckstea +beckwith +becky +beconovi +becquart +bedard +bede +bedford +bedi +bedient +bedlingt +bednar +bedoya +bedrosia +bee +beebe +beeby +beecker +beehler +beekman +beeman +beerkens +beers +bees +beeston +beethove +beeton +befanis +beffert +beggs +begley +begum +behdad +behlen +behler +behm +behnam +behrens +behroozi +behrouz +behzad +beil +beilin +beilul +beine +beique +beisel +beitinja +beitris +bejar +bekkedam +bekki +bektas +bel +bela +belair +belaire +beland +belanger +belboul +belcher +belcourt +belen +belford +belia +belich +belicia +belinda +belir +belisle +belissa +belita +belk +bell +bella +bellanca +belle +bellefeu +bellehum +bellevil +bellew +belley +bellina +bellingt +bellis +bello +bellosa +belmont +belohoub +belrango +belson +belton +beltran +belva +belvia +belyaev +belzile +bemiller +bemis +ben +ben-isha +benabdal +benasso +benavide +benay +benchimo +bencia +benda +bender +bendick +bendicty +bendite +bendix +beneda +benedek +benedett +benedick +benedict +benedikt +benefiel +benefits +beneteau +benetta +benfield +benge +bengt +bengtson +benham +beniamin +beninger +benita +benito +benjamen +benjamin +benjavan +benjes +benji +benjie +benjy +benn +bennatt +benne +bennefel +benner +bennesa +bennet +bennett +benni +bennie +benning +bennison +benny +benoit +benoite +benschop +benski +benson +bent +benthem +benthin +bentlee +bentley +bento +benton +benwell +benyamin +benyon +benzick +benzie +beom-sah +beomsahn +beorn +beowulf +bep +beppie +ber +beranger +berek +berenbac +berenice +berenz +beresfor +beresnik +beret +bereza +bergado +berger +bergeron +bergeson +berget +bergland +bergman +bergmann +bergquis +bergsma +bergstro +bergwerf +berhane +beriault +berk +berke +berkeley +berkie +berkley +berkly +berknet +berky +berman +bermel +bern +berna +bernaden +bernadet +bernadin +bernard +bernardi +bernardo +bernarr +bernd +berndt +berne +berneche +bernelle +berneta +bernete +bernetta +bernette +bernhard +berni +bernice +bernie +berniece +bernier +berning +bernita +berno +bernstei +berny +berri +berrie +berrin +berrisfo +berro +berry +berryhil +bert +berta +berte +berteau +bertha +berthe +berthele +berti +bertie +bertigno +bertina +bertine +bertini +bertolin +berton +bertram +bertrand +berty +berube +beryl +beryle +beshai +besharah +beshir +besime +besnier +bess +besse +bessel +bessell +bessette +bessey +bessie +besson +bessuill +bessy +bestavro +beswick +betcher +beth +bethanne +bethany +bethena +bethina +bethune +beton +betsey +betsill +betsy +betta +bettadap +bette +bette-an +betteann +betterle +betters +betti +bettie +bettina +bettine +bettink +betts +betty +betty-an +bettye +beulah +beun +beuren +bev +bevan +beveridg +beverie +beverlee +beverley +beverlie +beverly +bevin +bevingto +bevis +bevon +bevvy +bevyn +beware. +beygui +beymer +bezanson +bezdel +beznowsk +bhagvat +bhal +bhandari +bhanu +bharadwa +bharat +bhardwaj +bhasin +bhaskar +bhatia +bhatt +bhattach +bhatti +bhavani +bhoday +bhullar +bhupendr +bhupinde +bhusan +bi-jun +bi-shiou +biage +bialek +bialkeni +biamonte +bianca +bianchi +bianka +biard +bibbie +bibby +bibbye +bibekana +bibi +bible +bibr +bice +bickford +bidc +biddie +biddy +bidetti +bidget +bidyut +biederma +biegaj +biel +bielan +bielat +bielby +bielecki +bielejes +bienek +bienia +bierbrie +bierman +biermann +biersach +bieszcza +bigelow +biggers +biggerst +biggs +bigley +bigras +bihari +bihl +bijan +bijjani +bijman +bijons +bijun +bil +bilal +bilanski +bili +bill +billard +billi +billie +billing +billingh +billotea +billy +billye +bilodeau +bilovus +bilsboro +bilton +bimini +bin +bina +binda +binder +bindi +binette +bing +binggeli +bingham +bingley +bingwu +binh +bink +binkley +binky +binner +binni +binnie +binningt +binny +bins +biomecha +biomed +bipin +biplab +bir +biray +birch +bird +birdie +birendra +birgit +birgitta +birgitte +birk +birkett +birks +birkwood +birmingh +biron +birtch +bisad +bisch +bishiou +bishwa +bishya +bismark +biss +bissegge +bissette +bisson +bissonne +biswa +biswajit +bitar +bittenbe +bittman +bitton +bivens +bizga +bjorklun +bjorn +bjornson +blaauw +blackard +blackbur +blacker +blackley +blackloc +blackman +blacksha +blackshi +blackwel +blackwoo +bladon +blaikloc +blaine +blair +blaire +blais +blaise +blake +blake-kn +blakelee +blakeley +blakemor +blakesle +blakey +blakkolb +blalock +blanca +blanca-s +blancasi +blanch +blancha +blanchar +blanche +blanchet +blanco-a +blander +blane +blankens +blann +blaschuk +blasine +blasing +blasko +blatherw +blatt +blau +blauer +blaufus +blaylock +blayne +blazejew +blazek +blazer +bleile +blenk +blenkarn +blesi +blethen +bleuer +blevins +blezard +blidy +blimkie +blinn +blinni +blinnie +blinny +bliss +blisse +blissett +blithe +blodgett +bloedon +bloemker +blois +blomquis +blondell +blondie +blondy +bloodwor +blostein +blouin +blount +bluethne +blum +blumenfe +blumer +bluschke +bly +blyskal +blyszcza +blythe +bmethods +bnr +bnrecad +bnrinfo +bnrlsi +bnrsport +bnrtor +bo +bo-ping +boal +boaman +boarder +boase +boatwrig +bob +bobak +bobar +bobb +bobbe +bobbee +bobbette +bobbi +bobbie +bobbitt +bobby +bobbye +bobette +bobina +bobine +bobinett +boccali +bockaj +bocklage +bocservi +boddevel +boden +bodford +bodin +bodkin +bodnar +bodo +boeck +boecke +boehlke +boehms +boen +boer +boersma +boeyen +bogal +bogart +bogdan +bogert +bogey +boggan +boggia +boggild +boggs +bogumill +boguslaw +bohacek +bohanan +bohannon +bohdan +bohn +bohner +bohyun +boigie +boileau +boily +boinnard +bois +boisseau +boisset +boisvert +boivin +bojeck +bokanovi +bokij +bokish +boland +bolding +bolduc +boleda +bolen +boles +bolgos +bolio +bolli +bolly +bolon +bolouri +bolsinge +bolton +bolzon +bomba +bombardi +bommakan +bommer +bomstein +bon +bonahoom +bond +bondie +bondon +bonduran +bondy +bone +bonfanti +bongers +boniface +bonita +bonn +bonnar +bonneau +bonnee +bonnefoy +bonnell +bonner +bonnevil +bonney +bonni +bonnibel +bonnie +bonnin +bonny +bono +boocock +booker +booking +bookings +boon-sio +boone +boonie +boonphet +boonsion +boony +boorne +boorse +boos +boose +boot +boote +booth +boothe +boothroy +bophal +boping +bopp +boppana +bor-wen +bora +boraie +boray +borcic +bord +bordage +borden +bordie +bordin +bordy +borek +borel +borg +borgia +borha +boris +borivoje +borkowic +borman +borodajl +borojevi +borosch +borosh +boroski +boroughs +borowiec +borozny +borrelli +borsa +borsato +borson +bortenst +borthwic +bortolus +borum +boruslaw +borwen +borza +borzic +bosch +boschin +boscio +bosco +bose +bosiljev +bosiljka +bosko +bosnich +bosnyak +bossa +bossert +bossett +bossler +bostelma +bostock +boswell +boswick +bosworth +bosy +bot +bothwell +bott +botti +botting +bottis +botto +bottomle +bottoms +botyrius +bouchard +boucher +boucouri +boudin +boudreau +bouffard +bouick +boulais +boulay +bouleric +boulos +boult +bounds +bour +bourahla +bourbonn +bourcier +bourdeau +bourdign +bourdin +bouret +bourgaiz +bourgaul +bourget +bourgon +bourguig +bourk +bourke +bourland +bourlet +bourne +bouroncl +bourque +bourret +bousfiel +boutilie +boutin +boutniko +boutot +bovat +bovee +bovenize +bovey +bowab +bowcock +bowden +bowen +bowens +bower +bowers +bowes +bowick +bowie +bowler +bowles +bowling +bowser +bowyer +boy +boyachek +boyajian +boyce +boycey +boycie +boyd +boye +boyea +boyer +boyes +boylan +boyle +boynton +boz +bozeman +bozicevi +bqb +braaksma +brabant +brabec +bracewel +brackin +brackley +bracy +brad +bradan +bradbury +bradd +braddock +braddy +brade +bradee +braden +bradford +brading +bradlee +bradley +bradlow +bradly +bradnels +bradney +bradshaw +brady +bradyhou +bragado +braganza +bragg +braginet +braham +brahim +brahmana +brahms +brailey +brain +brait +brajesh +bram +brambley +bramlett +bran +brana +branchau +brand +brandais +brande +brandea +brandel +branden +brander +brandi +brandice +brandie +brandise +brandon +brandsen +brandsta +brandt +brandtr +brandvol +brandy +brandyn +branham +brann +brannan +brannen +brannick +brannon +brans +branscom +brant +brantley +brar +brashear +brasingt +brassard +brassell +brassem +brasset +brasunas +brathwai +bratten +brauer +brault +braum +braun +braunsti +braverma +brawley +brazeau +breanne +brear +brearley +breault +brechtje +bredeck +bredfeld +bree +breedlov +breena +bregitte +breglec +brehm +breisch +breiten +brekel +brel +bremner +bren +brena +brend +brenda +brendan +brenden +brender +brendin +brendis +brendon +brenn +brenna +brennan +brennand +brennen +brent +brentley +brenton +breon +brese +bresee +breslin +bresnaha +bresnan +bress +bret +breton +brett +breuer +brevard +brew +brewer +brewster +brewton +bria +brian +briana +brianna +brianne +briano +briant +briante +briard +brice +brichett +brickey +brickman +bride +briden +bridenst +bridge +bridgefo +bridges +bridget +bridgett +bridgman +bridie +brieda +briel +brien +brier +briere +brierley +brietta +brig +brigg +briggs +brigham +brightwe +brigid +brigida +brigit +brigitta +brigitte +brina +brind'am +brindley +briner +briney +bringhur +brinklow +brinkman +brinn +brinna +brintnel +brinton +briny +brion +brisby +briseboi +brissett +brisson +brit +brita +britman +britney +britni +britt +britta +brittain +brittan +brittane +brittani +brittany +britte +britteny +brittne +brittney +brittni +britto +britton +brivet +brivins +brkich +brnaba +brnaby +broadfoo +broadhea +broadwel +broberg +broca +brocato +brock +brockhou +brockie +brockleb +brockman +brockmey +brocksch +brocky +brod +broddie +broddy +broderic +broderse +brodfueh +brodgen +brodie +brodman +brodowsk +brody +brogden +brogdon +brogley +brok +brokaw +brombal +bromley +bron +bronec +bronk +bronnie +bronny +bronson +brook +brooke +brooker +brookes +brookhar +brookhou +brooks +brooksba +broome +brophy +broschuk +brose +brossard +brossela +brosso +brost +brostrom +broten +brothers +brothert +brough +broughto +brouille +broulik +broussar +broussea +brouthil +brouwer +brovont +brower +brown-gi +browne +brownfie +browning +brownlee +brownlie +brownrid +brox +broyles +brubaker +bruce +brucie +bruder +bruhl +bruin +bruis +bruketa +brule +brum +brummitt +brummund +brunato +bruncati +bruneau +brunel +brunelle +bruner +bruner-u +brunet +brungard +brunhild +brunke +brunner +brunner- +bruno +brunoni +brunstin +brunton +brushey +bruxvoor +bry +bryan +bryana +bryant +bryanty +bryce +brydges +brydon +bryenton +bryn +bryna +brynn +brynna +brynne +bryon +brys +bryttan +bse +bubak +bubel +buccella +bucci +buchan +buchanan +buchko +buck +buckalew +buckhoff +buckie +buckingh +buckley +bucklin +buckman +buckner +bucky +buczek +bud +buda +budd +buddie +buddy +buder +budhram +budi +budihard +budimiro +bue +buechner +buehler +buettgen +buffam +buffett +buffy +buford +bugajska +bugajski +buggie +buhler +buhr +buhrkuhl +bui +building +buiron +bujold +buker +bukowski +bukta +buky +bulan +bulanda +bulbrook +bulengo +bulent +buley +bulger +bulifant +bulitka +bulka +bulkovsh +bullard +bullas +bullen +bulletin +bullett +bullinge +bullion +bulman +bulmanis +bulmer +bulz +bumgarne +bumstead +bunce +bundschu +bunker +bunn +bunner +bunni +bunnie +bunny +bunting +buntrock +bunzey +buratyns +burbage +burbidge +burcew +burch +burchat +burchby +burdett +burdette +burdick +burega +burek +burg +burge +burger +burgess +burgette +burgi +burgin +burgwell +burk +burkard +burke +burkepil +burkert +burkett +burkey +burkhard +burl +burleigh +burleson +burlie +burnaby +burnage +burnard +burness +burnet +burnett +burnette +burney +burnie +burns +burnside +burr +burrell +burrowes +burrows +burrus +burruss +burt +burtie +burton +burty +burwell +busby +buscagli +buscarin +busch +busche +buschelm +bushell +bushnell +bushnik +business +buskard +buske +buskens +busko +bussewit +bussey +buster +bustillo +busuttil +butch +butcher +butner +butta +butterfi +butters +buttrey +butts +butvich +buxton +buzz +buzzell +buzzy +bvworks +by don o +bycenko +byczko +bydeley +byer +byers +byeungwo +byk +bykowy +bylina +byoung +byoungin +byram +byran +byrann +byrd +byrgesen +byrl +byrle +byrne +byrnes +byrom +byron +byung +byungyon +cabaniss +cabi +cabot +cabral +cabras +cabrera +caceres +cacha +cachero +cacilia +cacilie +cad +cadd +caddric +cadeau +cadieux +cadshare +cadtools +cady +cadzow +cae +caesar +caffrey +caffry +cagatay +caglar +caglayan +cahill +cahra +cai +caie +cain +caine +caines +cairisti +cairns +caison +caitlin +caitrin +cakarevi +cal +calahorr +calc +calcote +calder +caldwell +cale +caleb +caleta +calhoun +caliboso +calica +calida +calis +calistro +calkins +calla +callagha +callahan +callan +callanan +callean +calleja +callende +callery +calley +calli +callida +callie +callos +calloway +cally +calmejan +calmenso +calow +caltride +calumet +calv +calva +calvary +calvin +calypso +calzaros +cam +camacho +camala +camblin +cambre +camel +camel-to +camella +camellia +cameron +camet +camey +cami +camie +camila +camile +camilla +camille +camilluc +camino +camirand +cammi +cammie +cammy +campagna +campanel +campara +campbell +campeau +camplone +campo +campos +canada +canadian +canavan +cancela +candace +candee +candelar +candi +candice +candida +candide +candie +candis +candra +candy +canete +canfield +cang +cann +cannatar +cano +cantlie +cantrell +cantwell +canuel +canute +capelle +capes +capindal +caple +caplinge +capobian +capostag +capozzi +capps +capretta +caprice +captives +caputo +car +cara +caralie +carandan +carbajal +carbonar +carbone +carboni +carbonne +carce +cardella +carden +cardozo +cards +care +career +careers +carella +caren +carena +caresa +caresani +caressa +caresse +carevic +carew +carey +cargill +cargnell +cari +caria +caridad +carie +carignan +caril +carilyn +carin +carina +carine +cariotta +carisa +carissa +carita +caritta +cark +carkner +carl +carla +carlberg +carldata +carle +carlean +carlebac +carlee +carleen +carlen +carlene +carleton +carlett +carley +carli +carlie +carlin +carlina +carline +carling +carlis +carlisle +carlita +carlo +carlock +carlos +carlota +carlotta +carlsen +carlson +carlton +carly +carlye +carlyle +carlyn +carlynn +carlynne +carm +carma +carmel +carmela +carmelia +carmelin +carmelit +carmella +carmelle +carmelo +carmen +carmenci +carmicha +carmina +carmine +carmita +carmody +carmon +carmona +carnegie +carney +carnogur +carny +caro +carol +carol-je +carola +carolan +carolann +carole +carolee +carolien +carolin +carolina +caroline +caroljea +carolle +carolus +carolyn +carolyne +carolynn +caron +carpenti +carpool +carr +carran +carranza +carree +carri +carrie +carriere +carrillo +carringt +carrissa +carrmtce +carrol +carroll +carron +carruthe +carry +carrye +carson +carsten +carstens +carswell +cart +carter +cartohl +carty +carufel +caruk +caruso +caruth +carvalho +carver +cary +caryl +caryn +cas +casadont +casalou +casandra +casanova +casar +casas +cascarin +case +casey +cash +cashin +casi +casie +casinovi +caskey +casler +casnji +casotto +caspar +casper +casperso +cass +cassady +cassandr +cassar +cassat +cassaund +cassese +cassey +cassi +cassian +cassidy +cassie +cassius +casson +cassondr +cassy +castaban +castell +castello +casten +castillo +casto +castongu +castro +castro-h +castrono +caswell +cat +catanach +catarina +cate +caterina +catering +cath +catha +cathal +catharin +cathe +cathee +catherin +catherwo +cathi +cathie +cathleen +cathlene +cathrin +cathrine +cathryn +cathy +cathylee +cati +catie +catina +catja +catlaina +catlee +catlett +catlin +cato +caton +catrina +catriona +catthoor +caty +cau +cauchy +caudill +caudle +cauthen +cauthers +cavan +cavanagh +cavanaug +cavasin +cavasso +caves +cavill +cavin +caviness +cavnar +cawley +caye +cayer-fl +cayla +cayless +cayouett +caz +caza +cazzie +cbabbage +cchaddie +cecco +cece +cecelia +cech +cecil +cecile +ceciley +cecilia +cecilio +cecilius +cecilla +cecily +cecon +ced +cedric +cefee +cegelski +ceil +cele +celene +celesta +celeste +celestia +celestin +celestyn +celia +celie +celina +celinda +celine +celinka +celisse +celka +celle +cello +cellucci +celso +celyne +cemensky +cen +cencier +centeno +center +centers +centis +centre +cepero +cepheus +ceponis +ceranic +cerberus +ceri +ceriel +cerny +cervante +cesar +cesaratt +cesare +cesario +cesaro +cescon +cesya +cetraro +cezary +chaaban +chaar +chabane +chabert +chabrat +chacko +chacon +chad +chadd +chaddha +chaddie +chaddock +chaddy +chadha +chadrick +chadwick +chafin +chafy +chagnon +chahal +chahram +chai +chai-seo +chaikows +chaim +chaiman +chaimson +chaintre +chaisupa +chak-hon +chakraba +chakrabo +chakrava +chalifou +chalker +challice +chalmers +chalton +cham +chamard +chamayou +chambers +chamblis +champath +champion +champsi +chamsi +chan +chan-jiu +chan-nan +chance +chancey +chanchal +chanchla +chanco +chand +chanda +chandal +chandan +chander +chandler +chandra +chandrak +chandran +chandras +chandru +chane +chang +chang-hs +changes +changho +changhsi +chanh +chanitr +chanjiun +channa +channan +channell +channen +chanonat +chanpong +chanshin +chansik +chantal +chantall +chantel +chantell +chao +chao-pin +chaoping +chapa +chapdela +chapen +chapin +chapleau +chaplin +chapman +chapmond +chappell +chappuis +chaput +char +charangi +charasse +charbonn +charchan +chardon +charee +charene +charest +charette +chari +charil +charin +chariot +charis +charissa +charisse +charita +charity +charko +charla +charlean +charlebo +charleen +charlena +charlene +charles +charlesb +charleto +charley +charlie +charline +charlino +charlins +charlot +charlott +charlsey +charlton +charly +charmain +charman +charmane +charmian +charmine +charmion +charness +charney +charo +charon +charron +charter +chartier +chartran +charyl +chas +chasalow +chase +chasse +chastity +chatard +chatchai +chatel +chatfiel +chatha +chatri +chatterl +chattert +chattoe +chattos +chau +chaudhar +chaudhry +chaudry +chaug-mi +chaugmin +chauhan +chaunce +chauncey +chaurasi +chaurett +chautems +chauvin +chavers +chaves +chavez +chavis +chawki +chawla +chaya +chaz +che +chea +cheal +cheatham +cheba +checinsk +checklan +chee +chee-yin +chee-yon +cheesema +cheesman +cheetham +cheevers +chel +chellapp +chelsae +chelsea +chelsey +chelsie +chelsy +chem +chen +chen-che +chen-chu +chen-jun +chen-msi +chenard +chenault +chenchun +chene +cheney +cheng +cheng-do +cheng-fo +cheng-ho +cheng-hu +cheng-ts +chengdon +chengfoo +chenghon +chenghun +chengtse +chengwei +chenier +chenmsie +chennett +chenowet +chenye +cheol +cheow-to +cheowton +chepregi +cher +chere +cherenso +cherey +cheri +cherian +cheriann +cherice +cherida +cherie +cherilyn +cherin +cherise +cherish +cherkas +cherlyn +chern +chernets +cherng +cherri +cherrier +cherrita +cherry +chertok +chervena +chery +cherye +cheryl +ches +cheshire +chesley +cheslie +chesser +chesteen +chester +chesterf +cheston +chet +chetan +cheuk +cheung +chev +cheval +chevalie +chevarie +chevy +cheyenne +chhabria +chi +chi-haw +chi-ho +chi-hua +chi-hung +chi-kai +chi-keun +chi-kwan +chi-man +chi-vien +chi-wen +chi-yin +chi-yuan +chia +chia-hoa +chia-hua +chiabaut +chiahoan +chiahuan +chiaki +chiamvim +chian +chian-fo +chianfon +chiang +chiang-h +chianghu +chiaoyun +chiarell +chiarra +chiavaro +chic +chick +chickie +chickori +chicky +chico +chie +chief +chieh +chiem +chien +chien-ch +chien-hs +chien-hu +chienche +chienchi +chienhsi +chienhue +chieu +chih +chih-chi +chih-hsi +chih-hua +chih-tsa +chihaw +chihchia +chihchie +chihhsia +chihhua +chihtsai +chihua +chihung +chiiwen +chik +chikai +chilausk +childerh +childers +childree +childres +childs +chilibec +chilton +chima +chin +chin-ho +chin-lin +chin-shu +chin-ten +chin-wen +chinfui +ching +ching-ch +ching-en +ching-fu +ching-lo +ching-ts +ching-yu +chingchy +chingen +chingfu +chingtsu +chingyun +chinh +chinhin +chinho +chiniwal +chinlin +chinn +chinnery +chinrung +chinshu +chinteng +chinwen +chiou +chip +chiquia +chiquita +chiracha +chisholm +chisolm +chitkara +chitnis +chitra +chityal +chiu +chiverto +chiwen +chiykows +chiyo +chiyuan +chlo +chloe +chloette +chloris +chmara +cho +cho-kuen +cho-lun +chochon +chocs +chod +choe +chohan +choi +chojan +chok +cholet +cholette +cholewin +chomik +chona +chonchan +chong +chong-ch +chong-ke +chong-la +chongcha +chongkeu +choo +choo-kan +choon +choon-li +choong +chopin +chopowic +chopra +choptovy +choquett +chorley +chorng +chotkows +chou +choudhur +chouhan +chouinar +chowhan +choy +choynows +chriisto +chris +chrisman +chrisoph +chrisse +chrissie +chrissun +chrissy +christ +christa +christab +christal +christan +christea +christel +christen +christer +christi +christia +christie +christin +christl +christof +christop +christos +christy +christye +christyn +chrisy +chronowi +chrotoem +chroust +chruscie +chrysa +chrysant +chrysler +chrystal +chryste +chrystel +chu +chu-chay +chu-chue +chua +chuah +chuan +chuan-hs +chuang +chuanhsi +chubb +chubby +chucho +chuchuen +chuck +chueh +chuen +chugha +chui +chuj +chuk +chukwuem +chul +chuming +chummun +chun +chun-li +chun-shi +chun-yen +chung +chung-ch +chung-kw +chung-li +chung-wo +chung-yo +chungen +chungjen +chungkwo +chunglin +chungpha +chungsik +chunkin +chunlan +chunli +chunlin +chunling +chunmei +chunmeng +chunn +chunshin +chunyen +chuong +chuq +churas +churchil +chwen +chychrun +chye +chye-lia +chytil +cia +ciampini +cianci +ciancibe +ciaralli +ciaran +ciaschi +ciccarel +cicchino +cicci +cicek +cicely +cicero +cicily +ciel +ciesiels +cieslak +cifelli +cifersky +cigay +cilka +cimarron +cimino +cimolai +cinar +cinda +cindas +cindee +cindelyn +cinderel +cindi +cindie +cindra +cindy +cinicolo +cinnamon +cinq-mar +ciocca +ciochon +cioffi +ciolfi +cipolla +circe +ciriaco +cirillo +cirilo +ciro +cirri +cirstofo +cirulli +cis @ w +cisco +ciskowsk +cisnews +cissiee +cissy +citarell +cities +citrin +cividino +cizmar +clacher +claggett +claiborn +clair +claire +clairmon +claise +clampitt +clancy +clapham +clapp +clara +clarabel +clarance +clare +clarence +claresta +clareta +claretta +clarette +clarey +clari +claribel +clarice +clarie +clarinda +clarine +clarise +clarissa +clarisse +clarita +clark +clark-st +clarka +clarke +clarkson +clary +clason +class +classes +claude +claudell +claudett +claudia +claudian +claudie +claudina +claudine +claudio +claudius +claus +claveau +claxton +clay +clayborn +claybour +claybroo +clayson +clayton +clea +cleary +cleavlan +cleere +clegg +clem +clemence +clemens +clement +clemente +clementi +clements +clemie +clemmie +clemmons +clemmy +clendeni +clenney +clennito +clentice +cleo +cleon +cleopatr +clerc +clerissa +clerkcla +clerke +cleroux +clesson +clestell +cletis +cleto +cletus +cleve +clevelan +clevey +clevie +clevon +cliff +clifford +clifton +clim +clincket +cline +clinger +clinkard +clint +clinteas +clinton +clio +clippert +clipsham +clites +clive +clo +clocklab +cloe +cloherty +clooney +cloris +closson +clost +clotilda +clough +clouthie +cloutier +clovis +clow +cloyd +cluett +clusiau +cly +clyde +clysdale +clyve +clywd +cmet +co +co-op +co-ordin +coady +coallier +coathup +coats +cob +cobaugh +cobb +cobban +cobbie +cobbold +cobby +coble +cobley +cobo +cobran +cocco +cochran +cochrane +cockburn +cockcrof +cocke +cockins +cocos +cocos-ar +codack +codata +coddingt +code +codee +coder +codi +codie +codoc +codringt +cody +coe +coertnik +coffey +cogan +cogdell +coggins +coghlan +cogwell +cohea +cohen +cohn +cohn-sfe +cohoe +cohrs +coila +cointon +coker +cokol +colagros +colan +colangel +colanton +colas +colatta +colbert +colbourn +colburn +colby +colclasu +coldwell +cole +coleen +colella +coleman +colene +coles +colet +coletta +colette +coley +colford +colgan +colin +colina +colinda +collamer +collamor +collazo +collecut +colledge +colleen +collen +collete +collette +collevec +colley +colli +collie +collier +collin +colline +collins +collis +colly +collyer +colm +colman +coloads +colonton +colpitts +colquett +colquhou +colquitt +colston +colter +colterma +colton +colucci +colver +colvin +colwell +comay +combaz +combee +combella +combos +combs +comeau +comley +comm +commazzi +comments +committe +commons +communic +comp +compton +computin +comstock +comtois +con +conan +conant +conboy +concetta +concetti +conchita +concklin +concordi +conde +condell +condurel +conerly +coneybea +cong +congdon +congress +conistis +conklin +conley +conlin +conlon +conn +connell +connelly +conner +conners +conney +conni +connie +connolly +connor +connors +connors- +conny +conoly +conrad +conrade +conrado +conrath +conroy +consalve +conservi +consolat +constabl +constanc +constant +construc +consuela +consuelo +consulta +containi +contardo +conte +contine +contomic +conway +coochey +coody +coogan +cooke +cookie +cooksey +cooley +coolidge +coombs +cooney +coop +cooper +cooperma +coord +coordina +coors +copeland +copello +copeman +copes +coplesto +copley +copp +coppedge +coppins +coqueugn +cora +corabel +corabell +corace +coral +coralie +coraline +coralyn +corbeil +corbet +corbett +corbie +corbin +corbitt +corby +corcoran +cord +cordelia +cordelie +cordell +cordes +cordey +cordi +cordie +cordula +cordy +core +coreen +corella +corena +corenda +corene +coretta +corette +corey +cori +coriaty +corie +corilla +corina +corine +corinna +corinne +coriss +corissa +corker +corkey +corkigan +corkstow +corkum +corless +corlett +corley +corliss +corly +cormac +cormier +cornall +cornaro +cornel +cornela +cornelia +cornelis +corneliu +cornell +cornelle +corner +corney +cornie +corny +corpenin +corpuz +corr +correa +correia +correna +correy +corri +corriann +corrie +corrigan +corrina +corrine +corrinne +corritor +corrivea +corry +corsale +corse +corson +cort +cortie +cortland +cortney +corty +corvo +cory +cos +cosburn +cosentin +cosetta +cosette +cosgrove +cosimo +coslas +cosme +cosmo +cosner +cosola +cossota +costa +costache +costadim +costandi +costanti +costanza +costanzi +costas +costas-d +coste +costello +costen +cote +cothran +cotnam +cotner +cotten +cottengi +cotter +cottingh +cottrell +cotugno +cotuna +coucopou +couey +coughran +coules +coulman +coulombe +coulson +coulter +coulterm +count +coupal +coupland +courches +couron +coursdev +coursey +coursol +courson +court +courtadm +courtena +courtlan +courtnay +courtney +courvill +couse +couser +cousinea +cousins +coutelli +coutinho +couto +coutu +couture +covach +coverdal +covey +coviensk +coville +covingto +cowan +cowart +cowell +cowen +cowick +cowley +cowling +cowlisha +cownie +cowper +coxall +coxe +coyle +coyne +cozart +cozmo +cozyn +cozzi +cpebach +cpm +cprs +crabb +crabe +crabtree +cracknel +craddock +crafton +craggie +craggs +craggy +craghead +craib +craig +craig-du +crain +cramer +cramm +crampton +crandall +cranford +cranston +crapco +crase +craver +crawford +crawhall +crawley +crawshaw +cray +craycraf +cre +creamer +crean +creane +creasey +creasman +creative +credico +credille +creech +creecy +cregan +creigh +creight +creighto +cremer +crepeau +crerar +creswell +crews +cribbs +crichton +crick +crickard +cricker +cricket +crigger +crin +crippen +cripps +cris +crisler +crissie +crissy +crista +cristabe +cristal +cristen +cristesc +cristi +cristian +cristie +cristin +cristina +cristine +cristion +cristoba +cristofa +cristy +criswell +critchle +crittend +crl.word +crocker +crockett +crogie +croiseti +croix +crolla +cromer +crommie +crompton +cromwell +cronan +cronin +cronk +cronkrig +cronkwri +crooks +croom +cropper +crosby +cross +cross-as +crossass +crossley +crosson +crosswel +croteau +crothers +crotty +crowder +crowe +crowell +crowle +crowley +croxall +croxford +crozier +crucefix +cruey +cruicksh +crumpton +crusoe +crutchfi +cruz +cruzado +cryoelec +crysta +crystal +crystalb +crystie +csaszar +csenar +csilla +csite +csma +csop +csr +csreport +csua +ctas +cthrine +cuany +cuauhtem +cubical +cubicle +cucchiar +cucci +cuccia +cucciole +cucuzzel +cuddihey +cuddihy +cuddy +cuellar +cuervo +cuffle +cuffling +cuggy +culberso +culberts +culbreth +culham +culkin +cull +cullan +cullen +culley +cullie +cullin +culliphe +cullum +cully +culmer +culp +culver +culverho +cummine +cumming +cummings +cummins +cumpston +cunanan +cung +cunha-go +cunningh +cuong +cuper +cupid +cupido +curcio +curley +curmon +curnow +curr +curran +currer +currey +currie +currier +currin +curry +curt +curtice +curtin +curtis +curtt +cusato +cushing +cushman +cusick +cusson +custer +custsupp +cusumano +cuthbert +cuthill +cutrufel +cutter +cuu +cwirzen +cy +cyb +cybil +cybill +cybotech +cycelia +cymbre +cynde +cyndi +cyndia +cyndie +cyndy +cynethia +cynthea +cynthia +cynthie +cynthy +cynthya +cyr +cyril +cyrill +cyrille +cyrillus +cyrine +cyros +cyrus +cytrynba +czappa +czarneck +czeban +czes +czeslaw +czychun +d'ambros +d'amico +d'amour +d'andrea +d'angelo +d'anjou +d'anne +d'antoni +d'aoust +d'arcy +d'cruz +d'ingian +d'ippoli +d'lima +d'onofri +d'orazio +d'silva +d'soto +d'souza +da +da gama +da silva +da-shih +daaboul +dacal +dace +dacey +dach +dachelet +dacia +dacie +dack +dacre +dacy +dada +dadalt +dadang +dade +dadgar +dadkhah +dae +daebum +daedalus +dael +daena +daesik +daffi +daffie +daffy +dafoe +dag +dagama +dagenais +dagert +dages +dagg +dagley +dagmar +dagnall +dagnaw +dagny +dagoulis +dahai +dahan +dahi +dahl +dahlia +dahlstro +dai +daigle +daigneau +daijavad +daile +dailey +daimee +dairin +daisey +daisi +daisie +daisy +dajerlin +dal +dale +dalenna +dales +daley +dalia +dalila +dalip +dalis +dall +dall'ost +dallago +dallaire +dallal +dallas +dalli +dallis +dallon +dalmard +daloris +dalrympl +dalsiel +dalston +dalt +dalton +daly +damara +damaris +dambenie +dame +damena +damerji +damian +damiano +damien +damil +damita +damon +damone +dan +dana +danagher +danai +danbrook +danchi +dancy +dando +danduran +dane +danell +danella +daneshza +danette +danforth +dangubic +danh +dani +dania +danial +danica +danice +danie +daniel +daniela +danielak +daniele +daniella +danielle +daniells +daniels +danika +danila +danilo +danilowi +daniluk +danit +danita +danjean +danker +danko +danling +dann +danna +dannel +danni +dannie +danny +dannye +dans +danserea +dante +dantu +dantzler +dany +danya +danyelle +danyette +danzeise +danzig +dao +daochuan +daoud +daoust +daphene +daphine +daphna +daphne +daquano +dar +dar-der +dara +darb +darbee +darbie +darby +darcange +darcee +darcel +darcey +darci +darcie +darcy +darda +darden +darder +dare +dareen +darell +darelle +daren +dares +dari +daria +darian +darice +darill +darin +darina +dario +darius +darko +darla +darleen +darlene +darline +darlingt +darlleen +darn +darnall +darnel +darnell +darold +daron +darou +darpa +darr +darrel +darrell +darrelle +darren +darrick +darrimon +darrin +darroch +darrol +darrow +darry +darryl +darsey +darshan +darshana +darshi +darsie +daruius +darveau +darwen +darwin +darwyn +darya +daryl +daryle +daryn +daryoosh +daryoush +das +dasch +dasd +dasha +dasharat +dashih +dasi +dasie +dasilva +dasinger +dasrath +dassani +dassie +dasya +dat +data +datacent +datas +datasupp +datema +dates +datha +datta +dattalo +dau +daudin +daugavie +daughert +daughtre +daunais +daune +dauphina +dautenha +dauteriv +dav +davalo +dave +daveen +daven +daveta +davey +david +david-ye +davida +davidde +davide +davidh +davidovi +davids +davidson +davie +davies +davin +davina +davinci +davinder +davine +davis +davison +davita +davon +davor +davy +dawe +dawit +dawkins +dawn +dawna +dawne +dawson +daya +dayal +dayberry +dayle +daymond +dayna +dayton +db +dba +dbase +dbs +ddavid +ddene +ddocdb +de +de anda +de baets +de beaum +de belen +de boer +de buda +de cecco +de chabe +de cours +de crist +de eliza +de grace +de hoog +de la +de leon +de los +de marco +de marti +de muinc +de salis +de souza +de toni +de varen +de vito +de vries +de wiele +de wilto +de witt +de witte +de-anna +de-boer +de-ying +de_konin +deacetis +deadwile +deagle +deak +deakin +dealmeid +dealto +deames +dean +deana +deanda +deane +deanm +deann +deanna +deanne +deans +deanza +dear +dearaujo +deardurf +deason +deathera +deatrick +deb +debadeep +debasish +debassig +debbi +debbie +debby +debee +debera +debernar +debi +debkumar +deblois +debnam +deboer +deboor +debor +debora +deborah +debord +debortol +debra +debrah +debrun +debrusk +debs +decacque +decaire +decapua +decarie +decasper +decca +decelles +deciccio +deck +decker +declan +decleir +decource +decourcy +decoursi +deczky +dedas +dede +dedie +dedra +deduk +dee +dee dee +deeann +deeanne +deedee +deek +deena +deep +deepak +deerdre +deere +deery +deetta +deevey +deeyn +defacend +defalco +defazio +defilipp +deford +deforeit +defrance +defranch +degan +degen +degenova +degraauw +degrandi +deguines +deguire +dehaan +dehghan +dehlia +dehner +dehoff +dehr +deiadrel +deibert +deicher +deidre +deikman +deina +deininge +deirdre +deitera +deitiker +dejan +dejongh +dekai +dekeyser +del +dela +delaat +delage +delahay +delainey +delancey +delaney +delangis +delano +delargy +delat +delbert +delbret +delbridg +delbrouc +delcina +delcine +deleon +delf +delfin +delfreda +delgass +delgross +delia +deligdis +delila +delilah +delinda +delisle +deliva +dell +della +delle +delli +dellinge +delly +delmar +delmer +delmor +delmore +delnaz +delo +delolmod +delong +delora +delorenz +delores +deloria +deloris +delorme +delphine +delphini +delroy +deluca +deluce +deluco +delvecch +delzer +demarco +demarest +demchuk +dement +demeo +demers +demet +demeter +demetra +demetre +demetri +demetria +demetric +demetrio +demetris +demetriu +demeulem +demi +demidenk +demir +demjen +demmel +demone +demontlu +demorest +demorge +demott +demps +dempsey +dempster +demren +demuth +den +dena +dendi +dene +denebeim +deneen +denemark +denery +denest +denette +deng +deng-jyi +dengjyi +deni +denice +deniece +denike +denis +denise +denison +deniz +denley +denman +denmark +denna +dennen +dennet +denney +denni +dennie +denning +dennis +dennison +denno +denny +deno +denomme +denoon +denter +denton +denver +deny +denys +denyse +denzil +deog +deok +deol +deonne +depalma +depeltea +depew +dephoure +deployme +depooter +dept +dept. +der +der-chan +der-shen +deraadt +deraaf +derby +derbyshi +derecki +derek +derenzo +derganc +deri +derick +derika +derin +derk +derluen +dermardi +dermot +derome +derosa +derose +derosenr +derrek +derrett +derrick +derrik +derril +derron +derry +dersheng +derward +derwin +dery +deryck +des +desai +desalis +desantis +desautel +desch +deschamp +deschiff +descotea +descotes +desdemon +desgrose +desharna +desi +design +desilets +desimone +desirae +desire +desiree +desiri +desjardi +desjarla +deska +deslande +deslauri +desmarai +desmond +desmund +desoer +desorbay +desourdy +despain +despault +despinic +desplanq +despres +desroche +desrosie +dessain +desser +destech +destefan +destry +detjens +detlef +detleff +detlev +detloff +detra +deugau +deugo +deutschm +dev +deva +devadas +devan +devarenn +devault +deveau +devel +developm +deven +devenny +devenyi +devenyns +devera +devere +devette +devgon +devi +devices +devin +devina +devincen +devine +devinne +devland +devlen +devlin +devon +devondra +devonna +devonne +devora +devore +devouges +devreeze +devy +dew +dewain +dewart +dewayne +dewey +dewi +dewie +dewit +dewitt +dewitte +dex +dexiang +dexter +deying +deyirmen +deyoung +dezbah +dezoete +dg +dhaliwal +dhansukh +dhanvind +dhar +dharam +dharmara +dharmawa +dhaussy +dhawal +dheeraj +dhillon +dhinakar +dhir +dhiraj +dhiren +dhuga +dhupar +di +di cosol +di giamb +di maso +di millo +di ninno +dia-edin +diaconu +diahann +dialout +diamond +dian +diana +diandra +diane +diane-ma +dianemar +diann +dianna +dianne +diannne +diarmid +dias +diaz +dibenede +dibler +dicaprio +dick +dickard +dickens +dickerma +dickerso +dickeson +dickford +dickie +dickinso +dicks +dicksie +dickson +dicky +didani +didar +didi +didier +didio-du +dido +diduch +didylows +diec +diederic +diederik +diedrich +diee +diego +diekman +diemel +dien +diena +diener +diep +diepling +dierdre +diersch +diesing +dieter +dietra +dietrich +dieu +dieuwert +difalco +diffee +diffie +difilipp +difrance +digby +digenova +digiacom +digilio +dignam +dijaili +dijian +diju +dikaitis +dikens +dilallo +dilan +dilen +dilip +dilkie +dill +dillabou +dillard +dilley +dillie +dillingh +dillon +dillow +dilly +diloreto +dilpreet +dima +dimarco +dimarzo +dimas +dimetry +dimillo +dimitra +dimitri +dimitrio +dimitry +dimoueri +dina +dinaband +dinah +dinalic +dincamps +dineke +dinesh +dinges +dingle +dingley +dingman +dinh +dinhtran +dinkel +dinker +dinnervi +dinneyla +dinnie +dinnin +dinny +dino +dinsmore +dinur +diogo +dion +dione +dionis +dionisio +dionne +dionysia +dionysiu +dionysus +dipace +dipak +dipasqua +diperna +dipierro +dipietro +dipper +dirac +diradmin +dirbm +dirck +diretto +dirienzo +dirilten +dirk +dis +disalvo +discenza +discours +discover +disessa +disher +dishong +disisto +disney +dispatch +dissinge +distribu +dita +ditecco +ditko +dittburn +divyesh +dix +dixie +dixon +djavaher +djenana +djordje +djuan +dmaac +dmitri +dms +dmsdb +dmsrtime +dmuchals +dnadoc +dniren +dnsproj +do +doak +doan +dobbing +dobbins +dobbs +dobby +doble +dobransk +dobrosla +dobry +doc +docherty +dockendo +doctorjo +document +doczy +doda +dodd +dodds +dode +dodgson +dodi +dodie +dodier +dodman +dodson +dody +doe +doemer +doerfel +doerksen +doernber +doerr +doggett +dohan +doherty +doi +doig +doing +dokken +dokuzogu +dolan +dolezal +dolf +dolginof +dolgov +doliska +doll +dolley +dolli +dollie +dolly +dolores +dolorita +dolph +dolson +dom +domains +domanico +domas +dombrosk +domenic +domenick +domenico +domeniga +dominado +domine +dominga +domingo +domingue +domini +dominic +dominica +dominick +dominik +dominiqu +dommety +don +dona +donaghue +donahee +donahue +donak +donal +donald +donaldso +donall +donalt +donator +donaugh +donavon +doncaste +doncell +donegan +donelan +donella +donelle +donetta +dong +dong-ik +dong-moo +dong-pyo +dongik +dongmoon +dongpyo +donia +donica +donielle +donis +donita +donkers +donleyco +donlon +donn +donna +donnajea +donnamar +donne +donnell +donnelly +donner +donnette +donni +donnice +donnie +donny +donoghue +donohoe +donohue +donovan +dood +doodeman +dooley +doolin +doolittl +door +doortje +dora +doraine +dorais +doraiswa +doralia +doralie +doralin +doralyn +doralynn +doran +doray +dordari +dorden +dore +doreen +dorelia +dorella +dorelle +dorena +dorene +doretta +dorette +dorey +dori +doria +dorian +dorianne +dorice +dorie +dorin +dorine +dorion +dorion-m +doriot +doris +doris-ha +dorisa +dorise +dorit +dorita +dormer +dorn +dornback +doro +dorolice +dorolisa +dorotea +doroteya +dorothea +dorothee +dorothy +dorotich +dorr +dorra +dorree +dorreen +dorrell +dorri +dorrie +dorris +dorronso +dorry +dorsey +dorthea +dorthy +dorval +dory +dosanjh +dosenbac +doshi +dosi +doskas +dosref +doss +dost +dot +dotan +doti +dotsey +dotson +dotti +dottie +dottin +dotty +doublesi +doucet +doucette +doud +douet +doug +dougall +doughert +doughty +dougie +douglas +douglass +dougy +dourley +douville +dov +dovel +dover +dovydait +dow +dowd +dowding +dowdy +dowell +dower +dowjones +dowker +dowling +downer +downes +downey +downing +downs +dowse +dowser +doy +doyle +doyon +dpierre +dpn +dpnbuild +dpnis +dpnlab +dpnq&a +dpp +dpu +dr.jones +dr.seuss +drabek +drachman +draco +dracula +draffin +dragan +dragana +dragert +dragnea +drago +draier +drakage +drake +drako +drane +dransfie +draper +drappel +draves +dray +drayton +dre +dreddy +dredi +dreisbac +drenan +drennan +drescher +dresel +dresser +dressler +drew +drewes +drexel +dreyfus +dreyfuss +driedger +drieka +drinnan +driscoll +drissel +driver +drjones +drobnik +drolet +dromgool +drona +drop-box +dropin +droste +drouin +drseuss +dru +drubld +druci +drucie +drucill +drucy +drud +drudy +drugi +drugs +drumhell +drumm +drummer +drummond +drusi +drusie +drusilla +drusy +druzeta +drwiega +dryer +dryfoos +drynan +du berge +du-tuan +duan +duane +dube +dubeau +dubee +dubey +dubman +dubois +dubose +dubreck +dubreuil +dubroff +dubroy +dubuc +duc +duchaine +ducharme +duchesne +ducic +dud +duda +dudas +dude +dudgeon +dudley +dueck +duenas +duensing +dueppen +duer +duff +duffie +duffin +duffney +dufford +duffy +dufloth +dufour +dufresne +dugal +dugald +dugar +dugas +duggan +duguay +duisman +duke +dukes +dukey +dukie +duky +dula +dulaney +dulce +dulcea +dulci +dulcia +dulciana +dulcie +dulcine +dulcinea +dulcy +dulin +duljit +dulmage +dulsea +dulude +dumais +dumas +dummer +dumont +dumouche +dumps +dun +dunajski +dunbar +dunc +duncan +duncan-s +dundin +dunfield +dung +dungan +dunham +dunik +dunkelma +dunker +dunlap +dunlay +dunlop +dunmore +dunn +dunne +dunnett +dunning +dunningh +dunnion +dunphy +dunsmore +dunson +dunstan +duong +dupaul +duplacey +duplan +dupont +dupras +dupre +dupree +dupuis +dupuis-m +dupuy +duquette +dur +durali +duran +durand +durant +durantay +durante +durham +durie +durling +durnford +durose +durousse +durovic +durrell +dursse +durward +duryonna +dusan +dusko +dusomos +duster +dusty +dutch +dutcher +duthie +dutil +dutt +dutta +duvarci +duxbury +duy +duyck +dvm +dvs +dwain +dwaine +dwayne +dwight +dwyer +dyan +dyana +dyane +dyann +dyanna +dyanne +dyba +dybenko +dyck +dyckman +dyess +dyke +dylan +dyment +dyna +dynah +dynie +dyrdahl +dysart +dyson +dziamba +dziawa +dziemian +dzioba +dzulkarn +dzung +eachelle +eada +eades +eadie +eadith +eadmund +eagle +eagles +eakes +eakins +eal +ealasaid +eales +eamon +eamonn +eansor +earl +earle +earlene +earles +earley +earlie +earline +early +earnest +earnhard +earnie +earps +eartha +earvin +easaw +eason +easson +easter +easterli +eastick +eastland +eastman +easton +eastreg +eastus +eastwood +eaton +eaves +eb +eba +ebara +ebata +ebba +ebbingha +eben +ebeneser +ebenezer +eberhard +eberle +eberlin +ebert +eberto +ebonee +ebony +ebrahim +eby +echols +eckardt +ecker +eckert +eckhart +eckler +ecklund +eckstein +ecocafe +econ +ecroyd +ed +eda +edan +edd +eddi +eddie +eddins +eddisfor +eddy +ede +edee +edel +edeline +edelman +eden +eder +edey +edgar +edgard +edgardo +edgette +edgreen +edi +edie +edif +edik +edin +edison +edistix +edita +edith +editha +edithe +ediva +edkins +edlene +edlin +edmison +edmon +edmond +edmonds +edmondso +edmonton +edmund +edmundo +edmunds +edmx +edmxtest +edna +edouard +edric +edsel +eduard +eduardo +educatio +eduino +edvard +edward +edwards +edwige +edwin +edwina +edwins +edy +edyta +edyth +edythe +efdal +effie +efland +efrain +efrem +efren +efron +efstrati +efthim +efthimio +eftychio +egan +egashira +egbert +egdorf +egerman +eggebraa +eggers +eggersgl +eggleton +egional +egli +egne +egner +egon +egor +ehab +ehi +ehlers +ehninger +ehrenfri +ehrenhol +ehrlich +ehrlichm +eicher +eide +eierstoc +eiji +eike +eiki +eiko +eileen +eilis +eimer +eimile +einarsso +einersen +einstein +einwohne +eirena +eirik +eisele +eisen +eisenach +eisenber +eisenhar +eisler +eisner +eisnor +eiswirth +eitner +ekaterin +ekiert +el +el-am +el-gueba +el-hawar +el-torky +eladio +elaina +elaine +elam +elana +elane +elayne +elbert +elberta +elbertin +elbeze +elbi +elchakie +elda +elden +eldin +eldon +eldoris +eldredge +eldreth +eldridge +eleanor +eleanora +eleanore +elec +electra +electric +electron +eleen +elefteri +elefther +elena +elene +eleni +elenore +eleonora +eleonore +elery +eleta +elex +eley +elfie +elfreda +elfredia +elfrida +elfrieda +elga +elgar +elgie +elgin +elhage +elhamahm +elhamy +eli +elia +elianora +elianore +elias +elicia +elie +eliezer +elihu +elijah +elin +eline +elinor +elinore +elio +eliot +elisa +elisabet +elise +eliseo +elisha +elissa +elita +eliud +eliza +elizabet +elizalde +elka +elkaim +elke +elkhayat +elkind +elkingto +elkins +elks +ella +ellacott +elladine +ellary +elle +elledge +elleke +ellement +ellen +ellene +ellens +eller +ellerey +ellerman +ellery +ellette +elli +ellie +ellinger +ellingto +elliot +elliott +ellis +ellison +ellissa +ello +ellryne +ellswert +ellswort +ellul +ellwood +elly +ellyn +ellynn +elmar +elmer +elmira +elmo +elmore +elms +elna +elnar +elnora +elnore +eloisa +eloise +elonore +elora +elpida +elroy +els +elsa +elsbeth +else +elset +elsey +elsi +elsie +elsing +elsinore +elson +elspeth +elston +elsworth +elsy +elting +elton +eluned +elva +elvera +elvert +elvin +elvina +elvira +elvis +elvyn +elwin +elwira +elwood +elwyn +ely +elyn +elyse +elysee +elysha +elysia +elyssa +elza +elzbieta +elzer +em +ema +emad +emalee +emalia +emami +emanatia +emanuel +emanuele +emdin-sp +emelda +emelen +emelia +emelina +emeline +emelita +emelyne +emer +emera +emerick +emerson +emery +emesh +emhart +emig +emil +emilda +emile +emilee +emili +emilia +emilie +emilien +emiline +emilio +emilios +emily +emlen +emlyn +emlynn +emlynne +emma +emmalee +emmaline +emmalyn +emmalynn +emmanuel +emmeline +emmell +emmerich +emmersto +emmert +emmery +emmet +emmett +emmey +emmi +emmie +emmit +emmons +emmott +emmy +emmye +emogene +emond +emory +emowilli +emp +empdb +employee +emr +emran +emrick +emro +emyle +emylee +emysta +encomend +endang +ende +endenbur +enderle +enders +enderton +endicott +endless +endot +endrys +endsley +enet +eng +eng-sion +engbert +engel +engelber +engelbre +engelhar +engin +engineer +england +englande +engle +engleber +englebri +engleman +englert +english +engman +engr +engracia +engsiong +engtv +enid +enis +ennis +enno +enns +enoch +enos +enrica +enrichet +enrico +enrika +enrique +enriquet +ensign +ensing +ensminge +ensor +enstone +entwistl +enver +environm +envoy +enzo +eoin +eolanda +eolande +eow +eperjesy +ephraim +ephrayim +ephrem +eppensti +epperson +eppich +epplett +epps +eprom +epstein +epting +eran +erasmus +erastus +erbach +erbilgin +erda +erdem +erdinc +erek +erena +erfani +ergle +erguven +erh-huan +erhard +erhart +erhhuan +eric +erica +erich +ericha +erichsen +erick +ericka +erickson +erics +ericsson +erie +erik +erika +eriks +eriksson +erin +erina +erine +erinn +erinna +erkan +erkel +erl +erland +erle +erlene +erler +erling +erma +ermanno +ermarkar +ermengar +ermentru +ermey +ermin +ermina +erminia +erminie +ermo +erna +ernaline +ernest +ernesta +ernestin +ernesto +ernestus +ernie +erning +ernst +erny +eroler +eros +errchend +errick +errol +erroll +ersch +ersil +erskine +ertan +ertha +ertl +erv +ervi +ervin +erwei +erwin +erwing +eryn +erzsebet +es +esam +esc +esch +eschen +escher +escherma +escobedo +escobido +escutin +esdras +esgate +esguerra +eshelman +eshghi +esi +esite +eskew +eskiciog +eskildse +esko +eslambol +esler +esliger +esma +esmail +esmaili +esmaria +esme +esmerald +esmond +esparza +espenson +espinosa +espinoza +esposito +espuna +esra +esry +essa +essam +esselbac +esser +essery +essie +essig +esson +essy +esta +estabroo +este +esteban +estegham +estel +estele +estell +estella +estelle +estep +ester +estes +estevam +estevan +estey +esther +estrella +estrelli +estridge +eswara +etan +etas +etchieso +etemad +eteminan +ethan +ethe +ethel +ethelber +ethelda +ethelin +ethelind +etheline +ethelred +ethelyn +ethier +ethingto +ethnolog +ethyl +etienne +etoh +etta +etten +etti +ettie +ettore +ettridge +ettson +etty +etu +etzell +eu +eubanks +euclid +eudora +euell +eugen +eugene +eugenia +eugenie +eugenio +eugenius +eugine +eula +eulalie +euler +eunchae +eung +euni +eunice +eunji +euphemia +eustace +eustacia +eustis +euy-soo +euysoo +euysung +ev +eva +evaleen +evalyn +evan +evandro +evangeli +evangelo +evania +evanne +evans +evanston +eve +eveleen +eveleigh +evelien +evelin +evelina +eveline +evely +evelyn +evelyne +even +evenson +events +everard +evered +everett +everette +everitt +evers +evert +evette +evey +evia +evie +evin +evita +evon +evona +evonne +evraire +evren +evvie +evvy +evy +evyn +ewald +ewan +ewanchyn +eward +ewart +ewasyshy +ewell +ewen +ewing +exner +ext +eyde +eydie +eyers +eyk +ezechiel +ezekiel +ezella +ezequiel +eziechie +ezmerald +ezra +ezri +ezzat +fab +fabe +fabella +faber +fabian +fabiano +fabien +fabienne +fabijani +fabio +fabris +fabrizio +fabry +facchett +facility +fadel +fadhel +fadi +fadj +fadlalla +fady +fadzilah +fae +faez +fafa +fafara +fagan +fagg +fagin +fahey +fahim +fahrenth +fahy +fai +faina +fainaru +fainecos +faiq +fair +fairclou +fairfax +fairfiel +fairleig +fairless +fairlie +fairman +fairy +faisal +faison +fait +faith +faiz +faizal +fajardo +falaki +falardea +falbee +falcao +falconer +faletti +faley +falicov +falito +falke +falkenst +falkner +fallah +fallahi +falletti +fallis +fallon +fallows +falquero +falt +faltens +fambroug +familiad +famke +fan +fanchett +fanchi +fanchon +fancie +fancy +fanechka +fang +fangio +fani +fania +fann +fanner +fanni +fannie +fanny +fansher +fantauzz +fanthome +fanty +fanus +fanya +faou +far +fara +faraday +farag +farago +farah +farahvas +faramarz +farand +farant +fares +fargis +fargo +farhad +farhan +farhang +farhat +farias +fariba +fariborz +farica +farid +faris +farlay +farlee +farleigh +farley +farlie +farly +farmer +farn +farnham +farnjeng +farnswor +farnum +farokh +farooa +farooq +farouk +farquhar +farr +farra +farrah +farrand +farranto +farrel +farrell +farren +farringt +farris +farrokh +farronat +farrow +farrukh +farshid +faruk +faruque +farzad +farzin +fasken +fast +fastfeat +fastmer +fastone +fastowl +fastpack +fataneh +fater +fatholla +fatica +fatima +fattarus +fattouh +faubert +faucette +faucher +faulhabe +faulkner +faun +faunie +faust +faustina +faustine +fausto +faustus +favell +favreau +favrot +fawaz +fawcett +fawn +fawne +fawnia +fax +fay +fayanne +faydra +faye +fayette +fayez +fayina +fayma +fayre +fayth +faythe +faz +fazel +fearless +featherm +feddeman +fedderse +feder +federica +federico +federiko +fedora +fedoruk +fedyk +fee +feeley +feeney +fehr +fei +fei-wen +fei-yin +feil +feild +feisal +feist +feitel +feith +feiwen +fekade +fekri +felczak +feld +feldberg +felder +feldman +felecia +felfli +felic +felicdad +felice +felicett +felicia +felicio +felicity +felicle +felike +feliks +felipa +felipe +felisha +felita +felix +feliza +felizio +felli +fellman +felske +feltman +felton +femke +fenati +fender +fenelia +fenez +feng +fenlason +fenn +fennell +fenner +fennesse +fenton +fenwick +feodor +feodora +fequiere +ferba +ferd +ferdie +ferdinan +ferdy +feregyha +fereidoo +ferelith +ferenc +ference +ferenz +fererro +fergus +ferguson +fergusso +feridoun +ferland +fermat +fermi +fermoyle +fern +fernald +fernan +fernand +fernanda +fernande +fernandi +fernando +ferne +ferner +ferrao +ferrara +ferraro +ferree +ferreira +ferrel +ferrell +ferrer +ferrero +ferriera +ferrin +ferris +ferriss +ferro +ferruzzi +ferstl +fetterma +fetting +fetzko +feutlins +fevre-re +fey +feyen +feynman +fi-john +fiann +fianna +fiaz +ficco +ficici +ficken +ficker +fickes +fidel +fidela +fidelia +fidelio +fidelity +fidole +fiegel +fieke +field +fielden +fielding +fields +fieldsup +fierthal +fiest +fifi +fifield +fifine +figura +fijohn +fikis +fikre +fil +filbert +filberte +filberto +fildey +filer +files +files ar +filia +filibert +filide +filion +filip +filippa +filippi +filippo +filis +filkins +fillmore +filmer +filmore +filpus +filson +fima +fin +fina +finak +finance +finane +finckler +findlay +findley +finkhels +finlay +finlayso +finley +finn +finnegan +finnerty +finney +finnie +finnigha +finnon +fintan +finucane +finzel +fiona +fionan +fionna +fionnula +fiore +fiorenze +fiorile +firas +firat +firdaus +firerobi +firment +firtos +fischer +fischett +fischler +fiset +fisette +fishenco +fisher +fishkin +fishman +fisico +fisopn +fisprod +fiszman +fitch +fiteny +fitness +fitz +fitzgera +fitzgibb +fitzpatr +fitzroy +fitzsimm +fixsen +flach +flagg +flaherty +flanagan +flanders +flann +flanner +flansbur +flatley +fleet +fleig +fleische +fleishma +flem +fleming +flemming +fleskes +fletch +fletcher +fleuchau +fleugel +fleur +fleurett +fleurima +fleury +flewelli +flexo +flicking +flin +flindall +flinn +flint +flintall +flo +floch +flook +flookes +flor +flora +florance +florante +flore +florella +florence +florenci +florenti +florenza +flores +florescu +florette +florez +flori +floria +florian +florida +florie +florina +florinda +florine +floris +florjanc +florri +florrie +florry +flory +flossi +flossie +flossy +flounder +flowers +floyd +floysvik +flss +fludgate +flueckin +fluet +fluney +flury +fluty +flying +flynn +foad +fobert +focht +focsanea +focus +fodell +foderaro +foeppel +foessl +foest +fogelson +foght +fogle +fogleman +fok +folashad +foldes +foley +follett +follick +follmer +folwell +fon +fondacar +fong +fonnie +fons +fonsie +fontaine +fontana +fontanil +fontanin +fony +fonz +fonzie +foods +foong +foos +forbes +forbrich +forbs +ford +forden +fordham +forecast +foreman +forese +forest +forester +forgeron +forghani +forgues +forland +formagie +forno +forouhar +forrest +forreste +forrette +forslund +forst +forster +forsythe +fortes +fortier +fortman +fortner +foss +foster +fothergi +fotini +fouad +foubert +foucault +fouchard +fougere +fouillar +fouke +fouletie +foulkes +four +fouret +fourier +fourkas +fournel +fourney +fournier +fouts +fowler +fowler-h +fowles +fowlkes +fowlston +fox +foxworth +fpsched +fqa +fraanky +fradette +fragnito +fraley +fralick +fralix +frampton +fran +franc +france +francene +frances +francesc +francese +franchot +francic +francine +francis +francisc +francisk +francisp +franckli +franckly +francky +franco +francoeu +francois +francyne +frangoul +franics +frank +franka +frankcom +frankenb +franki +frankie +frankle +franklin +franklyn +frankos +franks +franky +franni +frannie +franny +frans +fransis +fransisc +frantise +frants +frantz +franz +franza +franze +franzen +franzky +franzwa +frape +frasco +fraser +frasier +frasquit +fraties +frayda +fraze +frazer +frazier +fred +freda +freddi +freddie +freddy +fredek +fredelia +fredenbu +frederic +frederig +frederik +frederiq +fredette +fredi +fredia +fredimos +fredine +fredra +fredric +fredrick +fredrika +fredriks +free +freeburn +freedman +freek +freeland +freeley +freeman +freemand +freemon +freeth +freiberg +freida +freimark +freire +freiwald +freixe +freksa +fremont +french +frendo +frenette +freno +fretz +freud +frey +freya +freyermu +freyler +frezzo +fricker +fricks +fridel +frie +frieda +friedber +frieder +friederi +friedl +friedlan +friedman +friedric +frierson +friesen +frinel +frink +frisa +frischkn +frischli +frisk +friton +fritz +fritzie +frizado +frobel +froberg +frobishe +frodsham +froehlic +froncek +frondozo +fronsee_ +fross +frosst +froud +froukje +frucci +fruehauf +frumerie +fruscia +fryar +frydach +frydman +fryer +fscocos +fssup +ftpsites +fu +fu-sheng +fu-shin +fu-zong +fuchs +fucito +fugen +fujii +fujimaki +fujimoto +fujiwara +fukui +fukumoto +fukunaga +fulford +fulk +fulkerso +fullager +fuller +fullmer +fullum +fulmer +fulton +fulvia +fumerton +fumio +funamoto +funderbu +fung +funston +fuping +fuqua +furdoonj +furgerso +furlin +furlow +furmania +furnas +furrukh +furst +furuta +fusca +fusheng +fuson +fussell +fuzal +fuzong +fwp +fwpas +fwpco +fwpreg +fwptools +fyfe +fysh +fyske +gaal +gabato +gabbai +gabbard +gabbey +gabbi +gabbie +gabby +gabe +gabey +gabi +gabie +gaboury +gabriel +gabriela +gabriele +gabriell +gabrila +gaby +gach +gaconnie +gadbois +gadher +gadouchi +gadsby +gadzinow +gae +gaebel +gael +gaelan +gaertner +gaetan +gaetanin +gaetano +gaffney +gafford +gaftea +gagan +gage +gagne +gagnier +gagnon +gahan +gahir +gahlot +gahn +gahr +gahunia +gaiarsa +gaiger +gail +gaile +gailya +gaime +gainer +gaines +gaiotti +gaiser +gaitan +gaither +gajendra +gajewski +gajowiak +gal +galanaki +galasso +galbrait +galdwin +gale +gale +galen +galewski +galina +galipeau +gallaghe +gallais +gallard +gallegos +gallenbe +galligan +gallinge +gallion +gallman +gallo +gallops +gallouzi +galloway +galluzzi +galois +galt +galvan +galven +galvez +galvin +gama +gamal +gamaleld +gamaliel +gamarnik +gambrell +gamelin +gammage +gamsa +gan +ganadry +ganapath +gandhi +gane +ganesan +ganesh +ganeshku +gangnes +gangotra +ganguly +gani +gann +ganness +gannett +gannie +gannon +gannot +ganny +gans +gant +gantt +gapp +gar +gara +garald +garamvol +garan +garand +garay +garbis +garbish +garcia +garcia-l +garcia-m +gard +gardener +gardie +gardiner +gardner +gardy +gare +garee +gareis +garek +gareth +garey +garfield +garg +garguilo +gargul +gargulak +garik +garinger +garito +garland +garmon +garneau +garner +garnet +garnett +garnette +garney +garo +garold +garp +garrard +garrek +garret +garreth +garrett +garrick +garrik +garrot +garrott +garry +garth +gartley +gartshor +garv +garvey +garvin +garvy +garwin +garwood +gary +gascho +gascon +gasikows +gaskins +gaspar +gaspard +gasparo +gasparot +gasper +gass +gast +gaston +gasul +gateau +gateley +gater +gates +gateway +gatka +gattrell +gau-rong +gaube +gaudet +gaudet-m +gaudon +gaudreau +gaughan +gaul +gaulle +gault +gaultier +gaunsezl +gaurong +gause +gauss +gautam +gauthier +gav +gavan +gaven +gavens +gavidia +gavilluc +gavin +gavra +gavriel +gavriell +gawain +gawargy +gawdan +gawen +gawronsk +gawtrey +gay +gaye +gayel +gayelord +gayl +gayla +gayle +gayleen +gaylene +gayler +gaylor +gaylord +gayman +gaynor +gayronza +gazala +gazier +gazo +gdowik +ge +geadah +gean +gearalt +gearard +geary +gebhardt +gebhart +gebrael +gedas +geddes +gedeon +gedman +gedra +gedye +gee +gee-meng +geer +geert +geesman +geeta +geetha +geety +gehm +gehr +gehring +geiger +geir +geisler +geksong +gelais +geldrez +gelinas +gell +geller +gelling +gelo +gelya +gemmill +gen +gena +genae +gendre +gendron +gene +geneau +general +generalc +generato +genet +geneva +geneviev +genevra +genga +genge +genia +genie +genna +gennaro +genni +gennie +gennifer +genny +geno +genova +genovera +genovise +genowefa +gentes +gentzler +genvieve +geoff +geoffrey +geoffrio +geoffry +georas +geordie +georg +georgann +george +georgean +georgena +georges +georgesc +georgeta +georgett +georghio +georgi +georgia +georgian +georgie +georgina +georgine +georgio +georgiou +georgy +ger +gera +gerald +geralda +geraldin +geralene +gerard +gerardja +gerardo +gerassim +gerbec +gerben +gerber +gerda +gerek +gerenser +gergen +gerhard +gerhardi +gerhardt +gerhart +geri +gerianna +gerianne +gerick +gerik +gerladin +gerlich +gerlinsk +gerlt +germ +germain +germaine +germana +germano +germayne +germe +gernot +gerome +gerrard +gerri +gerrie +gerrilee +gerrit +gerritse +gerry +gershwin +gerstmar +gert +gerta +gerth +gerti +gertie +gertridg +gertrud +gertruda +gertrude +gertrudi +gerty +gervais +gervaise +gery +gerynowi +gesine +gesino +gessford +getchell +getoor +gettys +geuder +gewell +geyer +geza +ghadisha +ghaemi +ghaemian +ghaffari +ghandi +ghanem +ghangurd +ghani +ghantous +ghartey +ghasemia +ghassan +ghassem +ghatta +ghazi +gheciu +ghelardu +gheorghe +gherardo +ghidali +ghislain +ghobad +gholamre +ghorashy +ghosh +ghossein +ghulam +ghulati +gia +giacinta +giacobo +giacomo +giacopo +giallo +giamatte +giambatt +giambera +giampaol +gian +giana +giandome +giang +giani +gianina +gianna +gianni +giao +giap +giarritt +giavani +gib +gibb +gibbie +gibbins +gibbons +gibbs +gibby +gibeault +giblin +gibson +gidaro +gideon +gidget +gie-ming +giekes +gieming +gierka +giertych +giesbrec +gieschen +giese +giff +giffard +giffer +giffie +gifford +giffy +giggey +gigi +giguere +gigus +gihan +gihyun +gil +gilbert +gilberta +gilberte +gilberti +gilberto +gilberts +gilburt +gilchris +gilda +gilemett +giles +giliham +gill +gillan +gillard +gille +gillelan +gilles +gillespi +gillespy +gillet +gillette +gilli +gilliam +gillian +gilliard +gillie +gillies +gillig +gilligan +gillilan +gillis +gillon +gillot +gillstro +gilly +gilmore +gilmour +gilstorf +gimon +gin +gina +ginelle +ginest +ginette +ginetto +ginevra +ginger +gingeric +gingold +gingras +gingrich +gini +ginn +ginni +ginnie +ginnifer +ginny +gino +ginsberg +gint +gintaras +ginzburg +gio +gioffre +gionet +giordano +giorgi +giorgia +giorgio +giorgos +giotis +giovanna +giovanni +giovinaz +gipsy +giralda +giraldo +girard +giraud +girgis +giri +giridhar +girish +girotti +girouard +giroux +girvan +gisbert +gisela +giselber +gisele +gisella +giselle +gita +gittins +giuditta +giuhat +giulia +giuliani +giuliett +giulio +giuntini +giuseppe +giustina +giustino +giusto +gize +gizela +glad +gladi +gladstei +gladys +glancey +glanfiel +glaros +glasa +glaser +glasgow +glass +glasser +glast +glaszcza +glazer +gleason +gleda +gleditsc +glembosk +glen +glenda +glenden +glendon +glenine +glenn +glenna +glennie +glennis +glew +glickman +glidewel +glinka +glinski +glofches +glori +gloria +gloriana +gloriane +glornia +glory +glover +glowa +glucksma +glymph +glyn +glynda +glynis +glynn +glynnis +gnaeding +gnni +go +goangshi +goatcher +goba +gobeil +gobeli +goble +gockel +godard +godart +godcharl +goddard +goddart +godden +goddette +godfree +godfrey +godfry +godin +godina +godish +godiva +godley +godlingt +godo +godowsky +godse +godsoe +godwin +goei +goel +goell +goeltzen +goerss +goertz +goertzen +goethe +goetz +goff +gofron +goggin +goh +goheen +goin +goins +gokal +gokul +gokul-ch +golari +golas +golaszew +golczews +golda +goldarin +goldberg +goldenbe +goldenso +golder +goldfiel +goldi +goldia +goldie +goldina +goldman +goldmann +goldner +goldschm +goldstei +goldthor +goldwyn +goldy +golia +goliss +golka +goller +gollu +golshan +gombos +gomes +gomez +gomm +gong-lia +gonglian +goniotak +gonsalve +gonzaga +gonzales +gonzalez +gonzalo +goober +gooch +goodbar +goode +gooderha +goodfell +goodier +goodinso +goodman +goodner +goodridg +goodrow +goodson +goodwin +goofy +goold +gooley +goos +gopal +gopaul +gope +gopisett +goran +gorasia +goraud +gorberg +gord +gordan +gorde +gorden +gordie +gording +gordon +gordy +gorenflo +gores +gorfine +gorham +gorhum +goricane +goridkov +goring +gorius +gorlick +gorman +gorsky +gorton +gorzocos +goska +goss +gosselin +gosset +gostania +goswick +goszczyn +gotch +gotchall +goth +gothard +gothart +gottfrie +gottlieb +gottscha +gottstei +gou-don +goudon +goudreau +gougeon +gough +gouhara +goukon +gould +gouldson +goulet +goulette +goulfine +goupil +gourley +goutam +govind +govindan +govindar +govindas +gow-jen +gowan +gowda +gowens +gower +gowin +gowjen +gowl +gowland +goyal +goyer +goyette +goza +gozani +gozen +grabner +grabowsk +grace +gracen +gracey +gracia +gracie +graciela +gracinda +gracomda +gradeigh +grader +gradey +grading +grads +grady +graehme +graeme +graessle +graff +grafton +graham +graibe +graig +grainger +gram +graman +grame +gramiak +gran +granado +granata +grandboi +grande +grandmas +grandump +grandy +granfiel +grange +granger +granic +granner +grannie +granny +grant +grantham +granthem +grantley +granvill +graphics +grasman +grason +grassman +grata +gratia +gratiana +gratton +grau +grauer +grausso +gravelle +gravely +graver +graves +gravitt +gravitte +grawberg +gray +graybill +grayce +graydon +grayson +grazia +graziano +grazzini +greaney +greatest +greaver +greaves +grebil +grebner +greco +greeley +greenber +greene +greenfie +greenlee +greenstr +greenway +greer +greet +greg +gregaric +greger +gregg +gregge +greggory +grego +gregoire +gregoor +gregor +gregor-p +gregorio +gregoriu +gregorsk +gregory +grelck +grenier +grenon +grenvill +greszczu +gret +greta +gretal +gretchen +grete +gretel +grethel +gretna +gretta +grevelin +grevy +grewal +grey +greytock +gribbon +gribbons +grier +griet +grietje +griff +griffie +griffin +griffioe +griffith +griffy +grigg +griggs +grignon +grigsby +grillmey +grills +grimble +grimes +grimm +grimmell +grimshaw +grimsley +griner +grinham +grinnell +gris +griselda +grisoni +grissel +grissom +griswold +gritton +grixti +griz +groce +grochau +grodecki +groetsem +groff +grogan +grohovsk +groleau +grona +grondin +gronwall +grooms +grootenb +gros +grosh +grosjean +grosman +grosse +grossman +grossutt +groth +groulx +grove +grover +groves +grovesti +growden +growler +gruau +grubbs +gruber +grueneic +grueng +gruenhag +grund +gruska +gruszczy +gryder +grzegore +grzegorz +grzesik +gsite +gu +guajardo +gualteri +guan +guanglia +guangyou +guanyun +guarez +guarino +guarnera +guatto +guay +gubbins +gubenco +gucer +guciz +gudgeon +gudrun +guendole +guenette +guenever +guenna +guenther +guercion +guerette +guerin +guerrero +guerrier +guertin +guests +guevara +guglielm +gui +guido +guignon +guilbaul +guilbert +guilford +guilfoyl +guillaum +guillema +guilleme +guillerm +guillet +guillory +guilmett +guimond +guin +guindi +guindon +guinever +guinn +guinna +guinnane +guiqing +guirguis +guisler +guitard +guitaris +gulbrand +gulick +gulis +gulko +gullekso +gultekin +gulvin +gumb +gumbley +gummadi +gumperz +gun +gunadhi +gunar +gunars +gunaseke +gunawan +gundecha +gunderse +gunderso +gundes +gundlach +gundry +guner +gunfer +gung +gungor +gunilla +gunkel +gunn +gunnar +gunnells +gunner +gunshor +guntar +guntekin +gunter +gunther +guntvedt +guo +guo-jie +guo-qian +guoben +guochun +guojie +guoming +gupta +gupton +gur-arie +gurash +gurchara +gurdip +gure +gurer +gurevitc +gurgenci +gurica +gurjinde +gurjit +gurley +gurmeet +gurnam +gurney +gursahan +gurshara +gursin +gurvinde +gus +gusella +guss +gussi +gussie +gussy +gusta +gustaf +gustafso +gustafss +gustav +gustave +gustavo +gustavus +gusti +gustie +gustlin +gusty +gutcher +gutermut +guth +guthrey +guthrie +guthro +guthry +gutierre +guttman +guty +gutzmann +guy +guy-arbo +guylain +guylaine +guyot +guzman +gwen +gwenda +gwendole +gwendoli +gwendoly +gweneth +gwenette +gwenneth +gwenni +gwennie +gwenny +gwennyth +gwenora +gwenore +gwo-chun +gwo-hsin +gwochung +gwohsing +gwyn +gwyneth +gwynith +gwynne +gyenes +gyeongbe +gyger +gylys +gyoung +gypsy +gysel +gyula +gyurcsak +gyurcsik +gzl +ha +haack +haaksman +haas +habeeb +habel +habelrih +haber +haberman +habert +habib +hachador +hache +hachelle +hachey +hack-hoo +hacker +hackett +hacking +had +hadaway +haddad +hadden +haddow +hadel +hadi +hadiraha +hadlee +hadleigh +hadley +hadria +hadrian +hady +hadziome +hae-won +haerle +haertel +haether +haewon +hafedh +hafeezah +hafermal +hafiz +hafleigh +hagan +hagar +hage +hagen +hagenbuc +hager +hagerty +hagewood +haggar +haggart +haggarty +haggerty +hagglund +haghighi +hagley +hagstrom +hagwood +hahn +hai +hai-ning +hai-ping +hai-shun +haibo +haifang +haig +haigh +hailee +hailes +hailey +haily +haim +haimson +hainer +haines +haining +hainline +haiping +haire +hairil +haishung +hak-lay +hakala +hakan +hakansso +hakeem +hakim +haklay +hal +hala +halbedel +halbert +hale +haleigh +halejak +halet +halette +haley +half +halford +hali +halicki +halie +halimeda +halina +hall +hallamas +hallenbe +haller +hallett +halley +halli +hallie +halligan +halliwil +hallman +hallsy +hally +halovani +halpenny +halpern +halpin +halsey +halstead +halsy +haluk +halula +ham +hamachi +hamavand +hambali +hambone +hamdy +hameed +hamel +hamelin +hamid +hamidi +hamil +hamilton +hamish +hamlen +hamlett +hamlin +hamliton +hammad +hammel +hammerli +hammerme +hammond +hammonds +hamner +hamnet +hamori +hamoui +hampel +hampshir +hampson +hampton +hamra +hamsa +hamzeh +han +han-chie +han-co +han-fei +han-tak +han-van +hana +hanan +hanchieh +hanco +hancock +handel +handfort +handley +handoko +handschy +hane +hanel +haney +hanfei +hanford +hang-ton +hangbok +hanger +hangup +hanh +hanham +hanhb +hanhua +hani +haningto +hanja +hank +hankins +hanlan +hanley +hann +hanna +hannah +hanneke +hanneman +hanni +hannibal +hannible +hannie +hannis +hanns +hannula +hanny +hanrahan +hans +hans-pet +hansen +hanser +hansiain +hanson +hanspete +hansquin +hansraj +hansson +hantak +hanzel +hanzlice +hao +hao-nhie +hao-yung +haonhien +haoyung +happy +harabedi +harada +haralamb +harald +harapiak +harbert +harbord +harbottl +harbour +harcourt +hardage +hardcast +hardee +harderse +hardi +hardiman +hardin +harding +hardison +hardman +hardwick +hardy +hardyal +hardyck +hardyman +haren +hareton +hargadon +hargreav +hargrove +hargrow +hari +harianto +harihara +hariman +harinder +harish +harishan +harker +harkness +harlan +harland +harlen +harlene +harles +harless +harley +harli +harlie +harlin +harm +harman +harmeet +harmi +harmon +harmonia +harmonie +harmony +harms +harn +harold +haroon +harootun +haroun +haroutou +harp +harpal +harpe +harper +harpreet +harrawoo +harrell +harri +harrie +harriet +harriett +harringt +harriot +harriott +harris +harrison +harrod +harron +harry +harsch +harshad +harsham +harshava +harshfie +hart +harte +hartell +harter +hartford +hartgrov +hartin +hartkopf +hartland +hartleb +hartley +hartling +hartman +hartmann +hartmut +hartney +hartsell +hartwell +harty +hartzel +haruko +harv +harvard +harvey +harville +harvison +harwell +harwerth +harwilll +harwood +hasan +hasbrouc +hasegawa +hasen +hasham +hasheem +hashem +hashemi +hashim +hashimot +haskel +haskell +haskins +haslach +hasler +haslett +hasmukhb +hasnain +hassan +hassenkl +hassey +hassnzah +hassold +haste +hasted +hastic +hastie +hastings +hasty +hata +hatcher +hatchett +hately +hatfield +hathaway +hatridge +hattar +hatten +hatti +hattie +hattingh +hatty +hatz +hatzenbi +hau +haubert +hauck +hauersto +haufe +hauge +haughey +haughwou +haugrud +haupt +haurie +hause +hauser +hautanen +havelock +haveman +haven +haverkam +haverty +havis +hawes +hawi +hawk +hawken +hawker +hawkes +hawkin +hawkins +hawley +hawryluk +hawrysh +hawryszk +hawthorn +hayden +haydock +haydon +haydt +hayes +hayley +haylock +hayman +haynes +haynor +hayward +haywood +hayyim +haze +hazel +hazeldin +hazell +hazelrig +hazelton +hazem +hazen +hazenboo +hazlett +hdbright +hdi +he +heald +healey +heall +health-s +healy +heaney +hearn +hearnden +hearst +heath +heather +heaton +hebbar +hebe +hebert +heckbert +heckman +hector +heda +hedda +heddell +heddi +heddie +heddy +hedi +hedin +hedke +hedman +hedrich +hedrick +hedvig +hedvige +hedwig +hedwiga +hedy +hee +heeralal +heeten +hefferna +heffner +hegarty +hegelian +hehn-sch +heida +heide +heidebre +heidepri +heidi +heidie +heighton +heike +heikkila +heile +heilig +heiliger +heilsnis +hein +heindric +heinen +heinjus +heinke +heino +heinonen +heinrich +heinrick +heinrik +heinz +heinzing +heinzman +heisler +heitmann +hekel +heki +helaina +helaine +heldenbr +heleen +helem +helen +helen-el +helena +helene +heleneli +helenka +helfrick +helga +helge +helgelan +helio +helkaa +hella +hellberg +hellen +heller +hellerst +helli +hellmut +helluva +hellyer +helma +helms +helmut +helmuth +helmy +heloise +helpb +helpline +helsa +helseth +helstab +helton +helwege +helyn +hemant +hembrick +hemens-d +hemme +hemmerle +hemphill +hempinst +hempstea +henao +hench +henderso +hendra +hendren +hendrick +hendrik +hendrika +hendriks +hendry +hendryck +hendy +henein +heng +hengameh +hengda +hengevel +hengl +hengst +henk +henk smi +henka +henley +henline +henneber +hennebur +hennelly +hennessy +hennie +henninge +hennon +hennriet +henny +henri +henrie +henrieta +henriett +henrik +henrika +henrikse +henry +henryett +hensen +henshaw +hensley +henson +henstock +henthorn +hepburn +hephziba +heping +heppell +heppes +hera +herak +herb +herbel +herberge +herbers +herbert +herbie +herby +herc +hercule +hercules +herculie +here's t +heredia +heribert +hering +herlihy +herling +herm +hermack +herman +hermann +hermann- +hermanns +hermes +hermia +hermie +hermien +hermina +hermine +herminia +hermione +hermon +hermy +hernan +hernande +hernando +herndon +hernek +herner +herng-je +herngjen +hernon +hernzlia +herod +herold +heroux +herr +herrage +herralio +herre +herren +herrera +herrick +herring +herringt +herriott +herrmann +herron +herronal +herryjan +hersch +herschel +herscovi +hersee +hersh +hershber +hershel +herskovi +herta +hertha +hertler +hertzog +herve +hervey +herzig +hesche +hesham +hesk +hesketh +heslop +hess +hesse +hester +hesther +hestia +hetti +hettie +hetty +hetzel +heung +heunis +heurich +hew +hewage +hewe +hewer +hewet +hewett +hewie +hewitt +hewlet +hews +heybroek +heydon +heyer +heynen +heys +heystrae +heyward +heywood +hi +hiawatha +hibberd +hibler +hichem +hickerso +hickey +hickin +hickman +hickman- +hickox +hicks +hidaka +hideki +hideo +hiebsch +hien +hienz +hieronym +hiers +higginbo +higgins +higham +highet +highsmit +hight +hightowe +higuchi +hijab +hikita +hil +hilaire +hilario +hilarius +hilary +hilberma +hilbert +hilbig +hilda +hildagar +hilde +hildebra +hildegaa +hildegar +hilder +hildum +hildy +hilfinge +hill +hilla +hillard +hillary +hillel +hiller +hillery +hilliard +hilliary +hillidge +hillie +hillier +hillring +hills +hillson +hilly +hillyer +hilmi +hils +hilton +hiltz +hilwa +himanshu +himawan +himraj +hin-wai +hincher +hinchey +hinchley +hinda +hindle +hinds +hindson +hine +hiner +hines +hing +hing-fai +hingtgen +hink +hinkel +hinkins +hinkle +hinojosa +hinsdale +hinshaw +hinson +hinton +hinton-s +hinz +hinze +hipp +hippert +hipson +hirakawa +hiraki +hiram +hirayama +hiren +hirofumi +hirohama +hiroki +hiroko +hiromi +hiromu +hironaga +hirooki +hirose +hiroshi +hirotaka +hiroto +hirotosh +hiroyuki +hirsch +hirshman +hisaki +hiscoe +hiscott +hisham +hishchak +hisko +hislop +hitchcoc +hitching +hite +hitler +hitoshi +hiusser +hively +hixon +hixson +hjartars +hjorth +hlady +hlausche +hlinka +hm +hnidek +ho +ho-mu +hoa +hoa-van +hoadley +hoag +hoagland +hoang +hoare +hobard +hobart +hobbs +hoben +hobesh +hobey +hobgood +hobie +hobin +hoch +hochbaum +hochberg +hock +hockaday +hockster +hoctor +hocutt +hodder +hoddinot +hodedo +hodge +hodgens +hodges +hodgins +hodgkin +hodgkiss +hodgson +hoebart +hoeg +hoehling +hoehn +hoek +hoeksma +hoekstra +hoeler +hoelsche +hoequist +hoes +hoferek +hoffelt +hoffman +hoffmann +hoffmeis +hoffpaui +hoffsted +hofmann +hofmeist +hofstede +hofstett +hogan +hogeboom +hogg +hoggan +hoggatt +hogue +hohmeyer +hohn +hoi-kin +hojjat +holberry +holbrook +holcomb +holcombe +holcroft +hold of +holdaway +holden +holder +holdren +holesing +holinski +hollack +holland +hollande +hollands +hollbach +hollen +hollenba +hollenbe +hollenst +holleran +holley +holli +holliday +hollie +hollings +hollingt +hollingw +hollis +holliste +holloway +hollran +holly +holly-an +hollyann +holm +holman +holmans +holmer +holmes +holmquis +holness +holsclaw +holst +holt +holterma +holthaus +holton +holtz +holtze +holvey +holy +holz +hom +homa +homan +homayoon +homayoun +homer +homere +homerus +homonick +homu +hon +hon-kong +hon-son +honbarri +honda +honey +honeycut +hong +hong-che +hong-yuh +hongchen +hongtao +hongyuh +hongzhi +honkakan +honmun +honor +honoria +honson +honzo +hoog +hooi-lee +hooker +hooks +hoon +hooper +hoorman +hooshang +hooton +hoover +hopcroft +hope +hopf +hopkin +hopkins +hopkinso +hopley +hoppenwo +hopper +hopson +hoptoad +hoque +hor +hor-lam +horace +horacio +horak +horalek +horatia +horatio +horatius +horban +hord +hore +horemans +horgan +horianop +horkoff +hormoz +hornacek +hornbeck +hornbeek +hornburg +horne +horng +horngdar +horning +hornung +horowitz +horsfiel +horst +horstman +hort +horten +hortense +hortensi +horton +horus +horvath +horwitz +horwood +hosang +hosanna +hoscheid +hoseok +hoshi +hosier +hoskin +hosking +hoskins +hosneld +hossein +hosseini +hot +hotline +hotlist +hotson +hotta +houde +houdini +houghton +houk +houle +houn +hounsell +houssam +houssein +houston +hoverman +hovey +hovinga +how +how-kee +howald +howard +howarth +howat +howden +howe +howe-pat +howekamp +howell +howerton +howes +howey +howie +howlett +howley +howorth +howse +hoxie +hoy +hoyer +hoyt +hpldt +hpone +hq +hqs +hr +hrdata +hrenyk +hrinfo +hrubik +hruby +hrushowy +hruska +hrvatin +hsi +hsi-ho +hsiang +hsiao +hsiao-ch +hsiao-we +hsiao-yu +hsiaochi +hsiaosu +hsiaowei +hsiaoyun +hsieh +hsien +hsiho +hsin +hsin-li +hsin-shi +hsing +hsing-ju +hsinli +hsiung +hsketh +hspice +hsu +hsuan +hsueh +htd +hu +hua +hua-yuan +huai +huan +huan-yu +huanbo +huang +huasheng +huashi +huay-yon +huayuan +huayyong +hubal +hubbard +hubbell +hube +huber +huberman +hubers +hubert +huberto +hubey +hubie +hubley +huboi +hudai +hudak +huddlest +hudecek +hudepohl +hudgins +hudson +hudy +hudyma +huel-she +huelshen +huelsman +hueneman +huerta +huestis +huether +huetu +huey +huey-kuo +hueykuo +hufana +huffman +hugel +huggins +hugh +hughes +hughes-c +hughey +hughie +hughson +hugibert +hugo +hugues +huguette +huguin +huhn +hui +hui-chau +hui-neng +huib +huichaun +huifang +huineng +huiqi +huitt +huizhao +hukam +hulda +hulen +hulett +huligang +hulk +hulme +hulst +hultgren +hulversh +hulze +humbert +humberto +humboldt +hume +humenik +humenuk +humes +humfrey +humfrid +humfried +humiston +hummel +hummerst +humphrey +humphrie +hundries +huneault +hunfredo +hung +hung-kan +hung-win +hungkai +hungkang +hungle +hungquoc +hungwing +hunike +hunnicut +hunsberg +hunsucke +hunt +hunter +huntingt +huntlee +huntley +huo-yen +huong +huor +huot +huoyen +hupe +huppert +hurd +huret +hurf +hurlee +hurleigh +hurley +hurman +hurst +hurtado +hurteau +hurtubis +hurwitz +husain +husam +husarewy +husein +hussain +hussam +hussein +husser +hussey +hussien +hustin +huston +huszar +huszarik +hutchers +hutchin +hutching +hutchins +hutchiso +hutson +hutt +hutter +hutton +huub +huuliem +huxley +huy +huyen +huynh +huyvan +huzur +hvezda +hwa +hwajin +hwan +hwang +hwayong +hwei-lin +hy +hyacinth +hyatt +hydar +hyde +hyen +hyer +hyerle +hyers +hyjek +hylaride +hyman +hymie +hynda +hyndman +hynek +hyong-ju +hyongjun +hyonil +hyoungju +hyperspa +hyrne +hysler +hyslop +hyte +hyun +hyunchul +hyung +i-chao +i-ching +iacoviel +iacovo +iago +iain +ian +ianace +iannotti +iannozzi +iantaffi +ianthe +iaquinto +iarocci +ibach +ibarra +ibbie +ibby +ibntas +ibrahim +ibsen +iburg +ic +iceman +ichabod +ichao +iching +ichiro +ichizen +icy +icylyn +id +ida +idalia +idalina +idaline +ide +idell +idelle +idette +idris +idt +idus +ie +iem +ientile +iezzi +if anyon +ifact +ifill +iftekhar +ifti +igarashi +iggie +igglesde +iggy +ignace +ignacio +ignacius +ignatius +ignaz +ignazio +igor +iguchi +igusa +ihnat +ihor +ijaz +ijff +ike +ikeda +ikey +ikotin +ikram +ikuo +ilaire +ilan +ilana +ilario +ilda +ileana +ileane +ilene +ilic +ilise +ilk +ilka +illa +illamchi +illidge +illinois +ilmberge +ilona +ilovich +ilowski +ilsa +ilse +ilsup +ilwhan +ilya +ilyas +ilyess +ilysa +ilyse +ilyssa +ima +iman +imbemba +imelda +imhof +imi +immanuel +imming +imogen +imogene +imojean +impaglia +imran +imre +imtaz +imtiaz +in-beum +in-cheol +in-hwan +ina +inam +inamulla +inan +inanc +inbeum +ince +incheol +incze +ind +indahl +indar +independ +inderjit +indiana +indianaj +indira +indra +indy +ineke +ines +inesita +inessa +inez +info +info-man +infocent +ing +inga +ingaberg +ingaborg +ingamar +ingar +inge +ingeberg +ingeborg +ingelber +ingell +ingemar +inger +ingersol +ingie +ingle +ingleber +ingles +ingling +inglis +ingmar +ingo +ingra +ingram +ingres +ingrey +ingrid +ingrim +ingunna +ingvar +inho +inhulsen +inhwan +inigo +inm +inman +inna +innchyn +innes +inness +innis +inniss +innocent +inoue +inquire +inrig +inscoe +insp +inspecti +instal +installe +integ +integrat +intemann +interact +interfac +intihar +intplan +intune +inyoung +ioan +ioana +ioannes +ioannidi +ioannis +ioannou +iocca +iocntrl +iola +iolande +iolanthe +iona +ione +iorgo +iorgos +iormina +iosep +ioui +ip +ipadmin +ipokrati +ippolito +iqbal +ira +iradj +iraj +irani +irby +irc +ircbellc +irccar +ircinter +ircmarke +ircmer +ircmtl +ircstand +irean +ireland +irena +irene +irfan +irias +iribarre +irice +irick +irina +iris +irish +irissou +irita +irma +irv +irvin +irvine +irving +irwin +irwinn +is a cat +isa +isaac +isaacs +isaak +isabeau +isabel +isabelit +isabell +isabella +isabelle +isac +isacco +isador +isadora +isadore +isahella +isaia +isaiah +isak +isami +isbister +iseabal +isenor +isensee +isert +isfan +ishak +ishan +ishee +isherwoo +ishii +ishikida +ishimoto +isiahi +isidor +isidora +isidore +isidoro +isidro +isin +isip +isis +iskandar +iskender +iskra +isl +islam +isley +ismael +ismail +isobel +isoft +isolde +israel +issa +issam +issi +issiah +issie +issy +itac +itah +italo +italus +itaru +itas +itaya +itch +iteam +iteke +ito +its-eng +iu +iva +ivan +ivancevi +ivancic +ivanhoe +ivanoff +ivanyi +ivar +ive +iver +ivers +iversen +iverson +ives +ivett +ivette +ivey +ivie +ivo +ivona +ivonne +ivor +ivory +ivy +iwan +iwanyk +iwashita +iwona +iws +iyad +iyengar +iyer +iyun +izaak +izabel +izak +izbinsky +izchak +izique +izora +izumi +izuru +izzat +izzo +izzotti +izzy +j-franco +jaakkola +jaan +jabbari +jabez +jabir +jablonsk +jabreen +jacalyn +jacek +jacenta +jachym +jacinda +jacinta +jacintha +jacinthe +jack +jackelyn +jacki +jackie +jacklin +jacklyn +jackman +jackquel +jackson +jacky +jackye +jaclin +jaclyn +jacob +jacobo +jacobs +jacobsen +jacobson +jacque +jacqueli +jacquely +jacquene +jacques +jacquett +jacqui +jacquie +jacynth +jacynthe +jada +jade +jadwiga +jae +jae-koo +jae-whan +jaekoo +jaenen +jaewhang +jaffer +jag +jagan +jagat +jagath +jagatic +jagdev +jagdish +jager +jagernau +jagjeet +jagjit +jagla +jago +jagodzin +jagriti +jags +jahangir +jahromi +jai +jaijeet +jaikne +jailyn +jaime +jaimie +jaimin +jain +jaine +jak +jakab +jakabffy +jakb +jake +jakeman +jaki +jakie +jakim +jakob +jakola +jakstys +jakubows +jalaie +jalal +jalali +jalaliza +jalbert +jalilvan +jama +jamaal +jamal +jamaly +jaman +jamel +jamensky +jamer +james +james_mi +jamesett +jameson +jamesy +jamey +jami +jamie +jamieson +jamil +jamilah +jamill +jamima +jamin +jamison +jammal +jammie +jammu +jamnejad +jamroz +jamshed +jamshid +jamshidi +jan +jan-olof +jan-robe +jana +janak +janaki +janaratn +janaya +janaye +jancewic +jancovic +janczyn +janda +jande +jandy +jane +janean +janecka +janeczka +janeen +janek +janel +janela +janell +janella +janelle +janene +janenna +janes +janessa +janet +janeta +janeth +janetta +janette +janeva +janey +jang +jang-hsu +janghsue +jani +jania +janice +janick +janie +janifer +janina +janine +janio +janis +janiszew +janith +janka +janke +jankowsk +jann +janna +jannay +jannel +jannelle +jannie +janning +janolof +janos +janot +janovich +janrober +janseen +jansen +janson +janssen +jantz-le +jantzi +januario +janusz +jany +jap +japan +japp +jaquelin +jaquelyn +jaquenet +jaques +jaquith +jarad +jarboe +jarchow +jard +jareb +jared +jarel +jargon +jarib +jarid +jarl +jarlath +jarmal +jarman +jarmo +jarmoc +jarmon +jarmul +jarnak +jarrad +jarred +jarret +jarrett +jarrid +jarrod +jarvah +jarvie +jarvin +jarvis +jarzemsk +jasbinde +jase +jasen +jashvant +jasmann +jasmin +jasmina +jasmine +jasny +jason +jasper +jaspreet +jasrotia +jasti +jastinde +jasun +jasver +jaswal +jatar +jatin +jatinder +jau-min +jau-yau +jaumin +jauvin +jauyau +java +javad +javallas +javar +javed +javier +javor +jawad +jawaid +jawana +jawanda +jawor +jaworski +jaworsky +jay +jaya +jayakuma +jayamann +jayant +jayanta +jayavant +jaye +jayendra +jayesh +jayjay +jayme +jaymee +jaymie +jayn +jayne +jaynell +jaynie +jayshree +jayson +jazanosk +jazmin +jcbach +jcst +jdavie +jderek +jean +jean-ber +jean-cla +jean-den +jean-fra +jean-guy +jean-jac +jean-lou +jean-luc +jean-mar +jean-mic +jean-nor +jean-pau +jean-pie +jean-rob +jean-roc +jean-yve +jeana +jeane +jeanelle +jeanes +jeanette +jeanhee +jeanice +jeanie +jeanine +jeanloui +jeanna +jeanne +jeannett +jeannie +jeannine +jeannot +jeany +jeavons +jecho +jed +jedd +jeddy +jedediah +jedidiah +jedrysia +jee +jee-howe +jeff +jefferey +jefferso +jeffery +jeffie +jeffrey +jeffreys +jeffries +jeffry +jeffy +jegland +jehanna +jehovah +jehu +jelen +jelene +jeleniew +jelinek +jelske +jem +jemczyk +jemie +jemima +jemimah +jemmie +jemmy +jen +jen-chen +jen-hua +jena +jenchen +jenda +jenelle +jeng +jenhua +jeni +jenica +jeniece +jenifer +jeniffer +jenilee +jenine +jenkins +jenkinso +jenn +jenna +jennee +jenner +jenness +jennette +jenni +jennica +jennie +jennifer +jennilee +jennine +jennings +jenny +jeno +jens +jensen +jensenwo +jenson +jeong +jephthah +jepson +jer-huan +jerad +jerald +jeralee +jeramey +jeramie +jere +jereme +jeremiah +jeremias +jeremie +jeremy +jerhuang +jeri +jermain +jermaine +jermayne +jernigan +jeroen +jerome +jeromy +jeronimo +jeroski +jerreld +jerri +jerrie +jerrilee +jerrilyn +jerrine +jerrold +jerrome +jerry +jerrylee +jervis +jerzy +jeska +jesper +jess +jessa +jessalin +jessalyn +jessamin +jessamyn +jesse +jessee +jesselyn +jessey +jesshope +jessi +jessica +jessie +jessika +jessup +jessy +jester +jesty +jesus +jet +jeter +jeth +jethro +jeurene +jew +jewel +jewell +jewelle +jewels +jewett +jey +jeyarara +jezioran +jhingran +ji +ji-chuu +jia +jia-wen +jiak-kwa +jiakkwan +jian +jianchen +jiang +jianli +jiann +jiann-ya +jiannyan +jianou +jianqi +jianxing +jianye +jianyun +jiawen +jiayi +jiayuan +jichuu +jie-yong +jiejie +jiethye +jieyong +jihad +jihan +jihyun +jiin-shu +jiinshuh +jilann +jilisa +jill +jillana +jillane +jillayne +jilleen +jillene +jilli +jillian +jillie +jilly +jim +jimenez +jiming +jiminy +jimmie +jimmy +jims +jimson +jin +jin-ho +jin-nan +jin-song +jin-yuan +jin-yun +jinann +jinchao +jindal +jing +jing-ru +jingbai +jinglun +jingru +jinho +jinhua +jinlun +jinn-kue +jinnan +jinnkuen +jinny +jinsheng +jinsong +jinsoo +jinyuan +jiri +jirina +jiro +jisang +jisheng +jitendra +jiuhuai +jivan +jiyuan +jiyue +jo +jo ann +jo-ann +jo-anne +jo-marie +joachim +joachimp +joan +joana +joane +joanie +joann +joanna +joannah +joanne +joannes +joannidi +joannie +joannis +joao +joaquin +job +jobe +jobey +jobi +jobie +jobina +joby +jobye +jobyna +jocelin +joceline +jocelyn +jocelyne +jochem +jochen +jock +jocko +jodee +jodi +jodie +jodine +jodoin +jodoin-s +jody +joe +joeann +joel +joela +joelie +joell +joella +joelle +joellen +joelly +joellyn +joelynn +joeph +joerg +joeri +joete +joey +joffe +johan +johan am +johan ch +johan se +johanama +johanchr +johann +johanna +johannah +johanne +johannes +johannse +johanseb +johansen +johanson +johathan +john +john-jr +john-pau +john-sr +johna +johnath +johnatha +johnatho +johnette +johni +johnna +johnni +johnnie +johnny +johns +johnsen +johnson +johnsson +johnston +johny +joice +joiner +joji +jojo +joke +jolanda +jole +jolee +joleen +jolene +joletta +joli +jolicoeu +jolie +jolin +joline +jolitz +joll +jolliffe +joly +jolyn +jolynn +jon +jonah +jonas +jonathan +jonathon +jonczak +jone +jonell +jonelle +jones +jong +jong-chi +jong-woe +jongchih +jonghun +jonghyuk +jongsun +jongwoei +jongwoo +joni +jonie +jonis +jonkheer +jonson +jonthan +joo +joo-euin +joo-geok +joon +joong +jooran +jooyul +joplin +jordain +jordan +jordana +jordanna +jordi +jordon +jorey +jorgan +jorge +jorgense +jori +jorie +joron +jorrie +jorry +jory +jos +josanne +joscelin +jose +josee +josef +josefa +josefina +joseito +joselito +joseph +josepha +josephin +josephs +josey +josh +joshi +joshia +joshua +joshuah +josi +josiah +josiane +josias +josie +josine +josip +joslin +joson +josselyn +jossine +josy +joubert +joudrey +jourdain +jourdan +jovo +jowett +joy +joya +joyan +joyann +joyce +joycelin +joydeep +joye +joyner +joyous +jozef +jozsef +jr +jsandye +jsbach +juan +juana +juanita +juarez +jubainvi +jubb +jubenvil +jubinvil +juby +jud +judah +judas +judd +jude +judge +judi +judie +judith +juditha +judithre +judon +judy +judye +judyresn +juergen +juers +jugandi +juh-shiu +juha +juhan +juhshiun +jui +jui-fen +juieta +juifen +juile +jukka +julayne +jule +julee +jules +juli +julia +julian +juliana +juliane +juliann +julianna +julianne +julianto +julie +julieann +julien +julienne +juliet +julieta +julietta +juliette +julina +juline +julio +julissa +julita +julius +jun +jun-li +junaid +june +juneau +juneho +junette +jung +jung-hua +junghua +jungmeis +juni +junia +junie +junina +junk +junkie +junkin +junli +junmeng +junzo +juozas +jurafsky +jurek +jurevis +jurewicz +jurg +jurgen +jurgens +jurgutis +juri +jurman +juscesak +juskevic +jussi +justen +justin +justina +justine +justinia +justinn +justino +justis +justo +justus +jusuf +jutta +jwahar +jyh-dong +jyh-doug +jyhdong +jyhdoug +jyoti +jyun-che +jyunchen +jyuo +kaare +kabe +kabel +kaboliza +kabuli +kac +kacey +kacie +kacor +kacsor +kacy +kaczmare +kaczmars +kaczynsk +kadah +kadamani +kaden +kadiyala +kadlecik +kaefer +kaehler +kaela +kaete +kagan +kah-ming +kahaleel +kahan +kahhale +kahhan +kahil +kahkonen +kahlil +kahn +kahneman +kahnert +kahtasia +kai +kai-bor +kai-ming +kai-wah +kai-wai +kaia +kaibor +kaiching +kaid +kaidanne +kaigler +kaila +kaile +kaileen +kailey +kain +kaine +kaiser +kaitlin +kaitlyn +kaitlynn +kaiwah +kaiwen +kaj +kaja +kajeejit +kaji +kakalina +kaki +kakou +kaksonen +kakuta +kala +kalab +kalai +kalaiche +kalair +kalappa +kalash +kale +kaleb +kalechst +kaleena +kales +kaley +kali +kalie +kalil +kalila +kalina +kalinda +kalindi +kaliski +kalitzku +kalle +kallewar +kalli +kallio +kally +kalman +kalnitsk +kalogera +kalpak +kalpit +kalra +kalsey +kalt +kalugdan +kaluzny +kalvin +kalwa +kalwarow +kalyan +kalyani +kalyn +kam +kam-hung +kam-suen +kamal +kaman +kamas +kambhamp +kambiz +kamboh +kameko +kamel +kamerson +kamhung +kamie +kamil +kamila +kamilah +kamillah +kaminsky +kamiya +kamiyama +kamlesh +kammerer +kamminga +kamol +kamoun +kamran +kamyar +kamyszek +kan +kan-hung +kana +kanagend +kanani +kanata +kanchit +kandace +kandappa +kandi +kandra +kandy +kane +kaneko +kaneshir +kang +kang-gil +kang-nin +kang-yua +kangelis +kangkun +kangning +kanhung +kania +kanies +kanika +kannan +kannel +kanneman +kanno +kansara +kant +kanthan +kantor +kanu +kanungo +kanwalji +kanwar +kanya +kao +kaoru +kaoud +kapadia +kapatou +kapella +kapil +kaplan +kapp +kappos +kaps +kapsa +kapsch +kapuscin +kara +kara-lyn +karaali +karademi +karalee +karalynn +karam +karan +karass +karattup +karchevs +kardomat +kardos +kare +karee +kareem +karel +karels +karen +karena +karhunie +kari +karia +kariann +karibian +karie +karil +karilynn +karim +karin +karina +karine +kariotta +karisa +karissa +karita +karkotsk +karl +karla +karlan +karlee +karleen +karlen +karlene +karlens +karlette +karlie +karlik +karlis +karloff +karlon +karlotta +karlotte +karlson +karly +karlyn +karmali +karmen +karmous- +karn +karna +karnazes +karney +karol +karola +karole +karolefs +karolien +karolina +karoline +karoly +karon +karp +karr +karrah +karrie +karry +karsan +karsner +karsz +kartik +kartikey +karunara +karwowsk +kary +karyl +karylin +karyn +kas +kasbia +kasbow +kasdorf +kasey +kashani- +kashef +kashima +kashul +kasifa +kaspar +kasparia +kasper +kasprzak +kass +kassam +kassandr +kassem +kassey +kassi +kassia +kassie +kassissi +kast +kastelbe +kasten +kastner +kaston +kasumovi +kat +kata +katalin +katarina +kataryna +katcher +katchmar +kate +katee +katerina +katerine +katey +kath +katha +katharin +katharyn +kathe +katherin +katheryn +kathi +kathie +kathleen +kathlin +kathnels +kathreri +kathrine +kathryn +kathryne +kathy +kathyb +kathye +kati +katibian +katie +katina +katine +katinka +katja +katleen +katlin +kato +katrin +katrina +katrine +katrinka +katsoura +katsumi +katsunor +katti +kattie +katuscha +katusha +katy +katya +katz +katzenel +kaudel +kauffeld +kauffman +kaufman +kaufmann +kaunas +kaura +kaus +kausche +kaushik +kavaler +kavanagh +kavid +kavis +kawa +kawabata +kawaguch +kawahara +kawakami +kawamura +kawashim +kawauchi +kay +kaya +kayaliog +kayar +kaycee +kaye +kayla +kayle +kaylee +kayley +kaylil +kaylyn +kayne +kaypour +kayser +kayvan +kaz +kazem +kazimier +kazmierc +kazue +kazuhiko +kazuhiro +kazuhito +kazuko +kazunori +kazuo +kazuyuki +kea +keala +kealey +kean +keane +kearney +kearns +keary +keast +keates +keating +keaton +keats +kebede +kechichi +keck +kedah +kedron +kee +keef +keefe +keefer +keegstra +keehan +keehn +keelan +keelby +keeler +keeley +keelia +keels +keely +keen +keenan +keene +keene-mo +keep +keer +kees +keever +keffer +kehler +kehoe +kehr +kei +keifer +keighley +keilholz +keilty +keim +kein +keinosuk +keir +keiser +keish +keisuke +keitel +keith +kejing +kelbe +kelbee +kelby +kelcey +kelci +kelcie +kelcy +keldon +kele +kelemen +kelessog +kelila +kelin +kelkar +kell +kelland +kellby +kelleher +kellen +keller +kellerma +kellett +kelley +kelleye +kelli +kellia +kellie +kellina +kellogg +kellsie +kellum +kelly +kelly-er +kellyann +kellyeri +kelner +kelsay +kelsch +kelsey +kelsi +kelso +kelsy +keltouma +kelvin +kelwin +kely +kem +kemal +kember +kemish +kemkeng +kemme +kemp +kempf +kempffer +kempler +kemppain +kempski +kempster +ken +kenda +kendal +kendall +kendel +kendell +kendi +kendra +kendre +kendrick +kenedi +kenik +kenji +kenkel +kenlan +kenmir +kenn +kenna +kennaday +kennan +kennard +kennedy +kenneth +kennett +kenney +kennie +kennith +kennon +kenny +kenol +kenon +kensinge +kent +kenta +kentaro +kenton +kenworth +kenyon +keogh +kepekci +kepler +ker +kera +kerby +kerensa +kerfoot +keri +keriakos +keriann +kerianne +kerith +kerk +kerley +kerlovic +kermie +kermit +kermy +kernahan +kernan +kerner +kernodle +kerns +kerr +kerri +kerri-an +kerrie +kerrill +kerrin +kerry +kerschen +kerschne +kerstin +kerwin +kerwinn +keseris +keshab +keshav +kesler +kesley +keslie +kesling +kessel +kessia +kessiah +kessing +kessler +kessley +kestelma +kester +kestutis +ketan +ketao +ketcham +ketcheso +ketchum +ketkar +ketley +ketsler +ketterer +ketti +kettie +kettles +ketty +keung +keuning +kev +kevan +keven +keveny +kevin +kevina +kevon +kevyn +keyes +keynes +keys +keyvan +khac +khachatr +khadbai +khai +khalaf +khaled +khalid +khalil +khalilza +khamdy +khanh +khanna +khanvali +khariton +khatib +khatod +khatri +khawar +khedkar +khesin +khezri +khieu +khim +khimasia +kho +khoa +khodosh +khoinguy +khon +khorami +khorrama +khosla +khosravi +khosro +khosrow +khouderc +khoury +khouzam +khue +khueh-ho +khuehhoc +khuon +khurana +khurshid +khyra +ki +kiah +kial +kiala +kiam +kian +kiang +kibler +kidd +kiebel +kiecksee +kiefer +kiel +kiele +kielstra +kiely +kiem +kien +kien-ngh +kienan +kiennghi +kiens +kieran +kiernan +kieron +kiersten +kiet +kieunga +kiger +kigyos +kihyen +kijin +kikelia +kiki +kikki +kiko +kikuchi +kikuta +kilbank +kilburn +kilby +kilcoin +kilcoyne +kile +kiley +kilgore +kilian +killam +killeen +killen +killer +killian +killie +killy +kilner +kilpatri +kilsaas +kilzer +kim +kim-elee +kim-minh +kim-stac +kim-tram +kimball +kimbarov +kimbell +kimberle +kimberli +kimberly +kimble +kimbo +kimbra +kimbrell +kimbroug +kimeleen +kimi +kimihiko +kimiko +kimio +kimler +kimm +kimma +kimme +kimmett +kimmi +kimmie +kimmo +kimmy +kimoto +kimstace +kimura +kin +kin-wai +kin-yee +kinahan +kinamon +kincaid +kinch +kindel +kindem +kindra +king +king-hau +kingaby +kingan +kingdon +kingrey +kingsbur +kingshot +kingslan +kingsley +kingsly +kingston +kingzett +kinh +kinley +kinman +kinna +kinnaird +kinney +kinnibur +kinnie +kinny +kinos +kinoshit +kinrys +kinsella +kinsey +kinsley +kinsman +kinson +kinstley +kinstry +kip +kipling +kipnis +kipp +kippar +kipper +kippie +kippy +kira +kirady +kirbee +kirbie +kirby +kirchner +kirchoff +kirfman +kiri +kirit +kirk +kirkby +kirkenda +kirkham +kirkland +kirkley +kirkpatr +kirkwood +kirley +kirn +kirouac +kirsi +kirsten +kirsteni +kirsti +kirstie +kirstin +kirstyn +kirt +kirtikum +kish +kishi +kishor +kishore +kissee +kissiah +kissie +kistner +kit +kita +kitajima +kitrick +kitson +kitt +kitti +kittie +kittinge +kitty +kitzmill +kivell +kiwon +kiyohara +kiyoharu +kiyoon +kizzee +kizzie +kjeld +kjell +klaas +klaassen +klammer +klamner +klapper +klapphol +klara +klarika +klarrisa +klashins +klasky +klassen +klatchko +klaudia +klaudiny +klaudt +klaus +klavkaln +klazien +klazina +klebsch +klein +klemens +klement +kleon +klepping +kletchko +klett +kleynenb +klier +klimas +kliment +klimon +kline +kling +klingspo +klink +klod +klodt +klosterm +kloth +klotz +klowak +klug +kluger +kluke +klutts +kmem +knapp +knappe +knapper +knapton +knecht +kneedler +kneese +kneeshaw +kneisel +knes-max +kness +knickerb +knieps +knighten +knighton +knio +knipe +knitl +knittel +knobeloc +knobloch +knorp +knorr +knouse +knowles +knox +knudsen +knut +knute +ko +ko-yang +koa +koang +koay +kobayash +kobeski +kobiersk +koblitz +kobreek +koch +kochansk +kochis +kodmur +kodnar +kodsi +kody +koelbl +koeller +koellner +koeman +koen +koenraad +koens +koerner +kogan +kogelnik +kohalmi +kohalmi- +kohl +kohler +kohm +kohn +kohnhors +kohut +koichi +koiste +koji +kok +kok-khia +kokkat +koko +kokoska +kokosopo +kolahi +kolappa +kolavenn +kolb +kolbe +koldinge +kolek +kolenda +kolesnik +koleyni +kolk +kolkka +kollen +koller +kollman +kollmorg +kolodiej +kolodzie +kolos +kolovson +kolski +kolton +koman +komaromi +komatsu +komenda +komorows +konarski +konda +kondagun +konforti +kong +kong-que +koning +konno +konomis +konrad +konradi +konstan +konstanc +konstant +konstanz +koo +koohgoli +koohi +koolstra +koolwine +koonce +koontz +kooyoung +kopala +kopell +kopfman +kopke +koprulu +kora +koral +koralle +koran @ +koray +korbe +korbel +korda +kordik +kordon +kordula +kore +korea +korean +korek +korella +koren +koressa +korest +korey +korf +kori +korie +korn +kornachu +kornegay +korney +kornitze +korpela +korrie +korry +kort +kortekaa +kortje +kory +kos +kosarski +kosasih +kosiorsk +kositpai +koskie +koskinen +koslowsk +kosman +kosnaski +kosowan +koss +kostas +kosten +koster +kostowsk +kosturik +kostyniu +kot +kotamart +kotaro +kotler +kotval +kotyk +kou +kou-yuan +kouba +kouhi +kouichir +kouidis +kouji +kouyuan +kovac +kovacs +koval +kovarik +kovats +koverzin +kowal +kowalcze +kowalesk +kowalkow +kowallec +kowalski +kowalsky +koyang +kozak +kozelj +koziol +kozlowsk +kozsukan +kozuch +kozyra +kpodzo +krabicka +kraehenb +krajacic +krajesky +krakowet +kramar +kramer +kranenbu +krater +kratz +krauel +kraus +krausbar +krause +krautle +krawchuk +krawec +kreiger +kreimer +krenn +krenos +kresl +kretsch +krick +kridle +krieg +kriegler +krienke +krier +kriko +krikoria +krinda +kring +kris +krisa +krisha +krishan +krishin +krishink +krishna +krishnah +krishnam +krishnan +krishnas +krispin +krissie +krissy +krista +kristal +kristan +kriste +kristel +kristen +kristi +kristian +kristie +kristien +kristin +kristina +kristine +kristjan +kristo +kristofe +kristoff +kristofo +kriston +kristoph +kristos +kristy +kristyn +kriton +krivossi +kriz +krodel +kroeger +krogh +krol +kroman +kromer +krone +krotish +krousgri +krowlek +krozser +krten +krueger +krug +kruger +krull +krummell +krumwied +kruse +kruuseme +kruziak +krym +krysia +kryski +krysko +krysta +krystal +krystall +krystle +krystn +krystyna +krzyszto +ktusn +ku +kuan +kuang +kuang-ts +kubash +kube +kubik +kubitsch +kuchelme +kuchinsk +kuchta +kucirek +kuczynsk +kudas +kudrewat +kue +kuechler +kuehn +kuehne +kuhfus +kuhlkamp +kuhn +kuhns +kui +kui-soon +kuivinen +kujanpaa +kulachan +kuldip +kulhy +kulik +kulikows +kulinski +kulkarni +kulman +kum +kum-meng +kumagai +kumamoto +kumar +kumares +kumi +kummer +kun +kun-ming +kundanma +kundel +kundert +kunecke +kung +kung-chi +kungchia +kunie +kunihiko +kunitaka +kuniyasu +kunjal +kunkel +kunming +kuntova +kunz +kunze +kuo +kuo-chua +kuo-feng +kuo-lian +kuochuan +kuofeng +kuoliang +kupe +kupfer +kupferma +kupfersc +kupidy +kupitz +kuracina +kurash +kurauchi +kurczak +kurdas +kurdziel +kure +kureshy +kurian +kurio +kurita +kurniawa +kurolapn +kurose +kurowski +kursell +kurt +kurth +kurtis +kurtz +kuruppil +kuryliak +kurylyk +kus +kusan +kushan +kushner +kushnir +kushwaha +kushwind +kusmider +kusum +kusumaka +kuswara +kusyk +kutac +kutch +kutger +kutschke +kutten +kuykenda +kuzbary +kuzemka +kuzyk +kuzz +kvochak +kwa +kwak +kwan +kwang +kwang-ch +kwang-lu +kwangchi +kwangchu +kwanglu +kwangsoo +kwant +kwast +kwee +kwei +kwei-san +kwiatkow +kwing +kwissa +kwock +kwok +kwok-cho +kwok-kin +kwok-lan +kwok-san +kwok-wa +kwokchoi +kwokkin +kwoksang +kwong +ky +kydd +kye-hong +kyehong +kyeong +kyla +kyle +kylen +kyler +kylie +kylila +kylo +kylynn +kym +kynthia +kyoko +kyomun +kyoon +kyoung +kyrie +kyrstin +kyu +kyu-sung +kyung +kyungchu +kyungyoo +kyusung +kyzer +l'anglai +l'ecuyer +l'heureu +l;urette +la +la verne +laale +lab +laba +labarge +labauve +labelle +labenek +laberge +labfive +labiche +labonte +labossie +labrador +labranch +labrie +labrinos +labuhn +lac +lacasse +lacee +lacelle +lacey +lachambr +lachance +lachine +lachowsk +lachu +lacie +lackenba +lackie +lackmann +lacombe +lacosse +lacoste +lacroix +lacy +ladan +ladasky +ladd +ladean +ladell +ladet +ladonna +ladouceu +ladva +ladymon +laetitia +lafarge +lafargue +lafata +lafayett +laferrie +lafever +lafferty +laflamme +lafleur +lafontai +laforge +laframbo +lafrance +lagace +lagache +lagarde +lagrande +lagrange +lahaie +lahaye +lahey +lahlum +lahteenm +lai +laidlaw +laila +laina +laine +lainesse +lainey +laing +laird +laitinen +lajzerow +laker +lakhani +lakhian +lakier +lakins +lakoff +lakshan +lakshmi +lakshmin +lalani +lali +lalibert +lalit +lalitha +lalka +lally +lalo +lalonde +lalu +lamar +lamarche +lamarque +lamarre +lambert +lambregt +lamedica +lamers +lamey +lamia +lamirand +lamm +lammond +lamond +lamonde +lamont +lamontag +lamoreux +lamothe +lamotte +lamouche +lamoureu +lampe +lampert +lamphier +lampman +lamport +lamy +lan +lana +lanae +lanava +lancaste +lance +lancelot +lanctot +land +landaver +landay +lande +lander +landers +landis +landman +landon +landriau +landry +lane +lanette +laney +lang +langdon +lange +langelie +langenbe +langer +langett +langevin +langford +langlais +langley +langlois +langner +langsdon +langstaf +langston +langton +lani +lanie +laniel +lanier +lanita +lankford +lanna +lannan +lanni +lannie +lanny +lanoe +lanoszka +lanoue +lanouett +lans +lansuppo +lanteign +lanthier +lantos +lantto +lantz +lanunix +lanwan +lanz +lanzkron +lao +lap +lapchak +lapierre +lapkin +laplace +laplante +lapointe +laporte +lapostol +lappan +laprade +lapre +laprise +lapsley +laquinta +lar +lara +larabie +laraia +laraine +larche +lareine +larese +lari +larimer +larin +larina +larine +larisa +larissa +larivier +lark +larkin +larkins +larmour +larn +larner +laroche +larock +larocque +larose +larribea +larrigan +larry +lars +larsen +larson +larstone +larue +laruffa +larus +larwill +lary +larysa +laryssa +las +lasch +laschuk +laser +laserjet +lash +lashansk +lasher +lashmit +lashonda +lask +laskaris +laskin +laslo +lasserre +lassig +lassiter +lasson +lassonde +laster +latashia +latchfor +latella +laten +latessa +latha +lathangu +lathrop +latia +latif +latin +latisha +latonia +latonya +latorre +latour +latreill +latrena +latrina +lattanzi +latulipp +lau +laubenhe +lauderda +laufer +laugher +laughlin +laughrid +laughton +launce +laura +lauraine +laural +lauralee +lauramae +laure +laureano +lauree +laureen +laurel +laurella +lauren +laurena +laurence +laurene +laurens +laurent +lauretta +laurette +lauri +lauria +lauriann +lauriaul +laurice +laurich +laurie +laurier +laurilyn +laurin +laurine +lauris +lauristo +lauritz +laurna +lauro +laursen +lauruhn +laury +lauryn +lauson +lauten +lauther +laux +lauze +lauzon +lavagno +lavallee +lavar +lavarnwa +lavecchi +laveda +lavelle +lavena +lavergne +laverna +laverne +lavers +laverty +lavictoi +lavictor +lavigne +laville +lavina +lavinia +lavinie +laviolet +lavoie +lavonda +lavorata +law +lawbaugh +lawler +lawless +lawlis +lawlor +lawrence +lawrie +lawry +laws +lawson +lawther +lawton +lay +layanand +layer +layla +layne +layney +layton +laz +lazar +lazare +lazaro +lazarou +lazarowi +lazarus +lazer +lazlo +lazure +lazzara +lcarrol +le +lea +leader +leads +leafloor +leah +leahy +leander +leandra +leang +leann +leanna +leanne +leanor +leanora +leaphear +leary +leatham +leathers +leaton +leatrice +leavell +leaver +leavitt +lebaron +lebars +lebbie +lebeau +lebel +leblanc +leblond +lebo +lebon +lecandro +lecien +leckie +leclair +leclaire +leclerc +lecompte +lecours +lecouteu +leda +ledamun +leddy +leder +lederman +ledet +ledford +ledinh +ledou +ledoux +ledu +leduc +ledwell +lee +lee-anne +leeann +leeanne +leecia +leela +leelah +leeland +leena +leendert +leenher +leesa +leese +leeson +leeuwen +lefebvre +lefevre +leffler +leftwich +lefty +legeny +leger +legg +leggett +legolas +legra +legrandv +legris +legros +legrove +legrow +legs +leguen +lehar +lehman +lehmann +lehrbaum +lehtinen +lehtovaa +lei-see +leia +leiba +leibich +leibovit +leibowit +leiceste +leicht +leidenfr +leiding +leif +leigh +leigha +leighann +leighton +leiker +leil +leila +leilah +leima +lein +leinen +leisa +leisha +leistico +leita +leitch +leite +leith +leitman +leitner +leitrick +leiwe +lek +lela +lelah +leland +lelia +lem +lemaire +lemar +lemay +lemieux +lemky +lemley +lemmie +lemmy +lemuel +lemyre +len +lena +lenard +lenathen +lenci +lendon +lenee +lenee' +lenehan +lenelle +lenette +leney +lengel +lenhard +leni +lenin +lenior +lenir +lenka +lenna +lennard +lennart +lennie +lennig +lenny +leno +lenora +lenore +lenox +lentz +leny +lenz +lenzi +leo +leo-miza +leocadio +leodora +leoine +leola +leoline +leon +leona +leonanie +leonard +leonardo +leonas +leone +leonelle +leonerd +leong +leonhard +leonid +leonida +leonidas +leonie +leonor +leonora +leonore +leontine +leontyne +leopold +leora +leoutsar +leow +lepage +lepine +lepore +leppert +lepreau +lerch +leres +leroi +leroux +leroy +les +lesa +lesco +lescot +leshia +leshowit +lesia +lesley +lesli +leslie +lesly +lesmeris +lesniak +lesourd +lesperan +lessard +lessin +lester +lesway +lesya +leta +letarte +letchwor +letendre +leth +letha +lethbrid +lethebin +lethia +leticia +letisha +letitia +letizia +letourne +letsome +lett +letta +lettang +letti +lettie +letty +letulle +leuenber +leung +leupold +leuty +lev +levac +levasseu +leveille +levent +levere +levert +levesque +levey +levi +levin +levine +levis +levisky +levitin +levo +levon +levy +lew +lewandow +lewek +lewellen +lewes +lewie +lewinski +lewis +lewiss +lewright +lex +lexi +lexie +lexine +lexis +lexy +ley +leyden +leydig +leyla +leyton +lezlee +lezley +lezlie +lheureux +li +li-ming +li-xi +lia +liad +lial +liam +lian +lian-hon +liana +liane +liang +liangchi +lianna +lianne +liao +lias +liason +liaurel +liaw +lib +libadmin +libbey +libbi +libbie +libby +liber +libor +libov +libraria +licandro +licata +lication +licerio +licha +licht +lichtenb +lichtens +lichum +lichun +licia +lida +liddell +liddle +lidia +lidio +lidster +lidstone +liduine +lieberma +liebrech +liedl +lief +liem +lien +lieneman +lienhard +liepa +liesa +liesbeth +liese +liesel +liesemer +liesenbe +liesie +liesl +lieure +lievaart +liew +lifshey +lightfie +lightfoo +lighthal +lighthis +lightowl +ligon +ligurs +lijphart +likert +likourgi +lil +lila +lilah +lilas +lili +lilia +lilian +liliana +liliane +lilias +lilin +lilith +lilla +lillenii +lilli +lillian +lillie +lillien +lillis +lilllie +lilly +lily +lilyan +limeina +limerick +limin +liming +lin +lin-chan +lin-e +lin-ni +lina +linas +linaugh +linback +linberg +linc +linchang +lincoln +lind +linda +linda-jo +lindamoo +lindberg +lindell +lindemul +linden +lindenla +linder +lindholm +lindi +lindie +lindler +lindon +lindow +lindquis +lindsay +lindsey +lindstro +lindsy +lindt +lindy +line +linea +linebarg +linegar +lineham +linell +linet +lineth +linette +linfield +ling +ling-hui +ling-yu +ling-yue +ling-zho +lingafel +linghui +lingyan +lingyu +linh +link +linke +linklett +linley +linn +linnea +linnell +linnet +linni +linnie +lino +linoel +linsley +linton +linus +linwood +linzie +linzy +lion +lionel +lionello +liou +lipari +lipe +liping +lippens +lippman +lipschut +lipscomb +lipski +lipton +lira +lisa +lisabeth +lisah +lisak +lisandro +lisbeth +lisch +lischyns +lise +lisee +lisenchu +lisetta +lisette +lish +lisha +lishe +liskoff +lisle +liss-mon +lissa +lissi +lissie +lissmoni +lissy +list +lister +liston +listonic +lita +litherla +litt +littau +littlewo +litva +litz +litzenbe +liu +liuka +liv +liva +livas +livek +livengoo +liverman +livermor +lives +livezey +livia +livingst +livinsto +livio +liviu +livnah +livshits +livvie +livvy +livvyy +livy +liwana +liwen +lixi +liyuan +liyun +liz +liza +lizabeth +lizak +lizbeth +lizette +lizz +lizzi +lizzie +lizzy +ljiljana +ljilyana +ljubicic +llacuna +llaguno +llanos +llewelly +llopart +lloyd +llywelly +lo +loa +loadbuil +loader +loadsum +loan +loarie +loay +lobasso +lobaugh +lobello +lober +lobianco +lobin +lobue +loc +locicero +lock +lockard +locke +locken +lockett +lockhart +lockwood +lococo +lodesert +lodovico +loe +loeffler +loeiz +loel +loella +loes +loesje +loewen +loftis +logan +logaraja +loggins +loghry +logntp +logue +loh +lohoar +loi +loire +lois +loise +loiseau +loisel +lojewski +loke +loker +lola +loleta +lolita +lollis +lolly +lombard +lombardo +lombardy +lombrink +lommen +lon +lon-chan +lona +lonald +lonchan +londhe +london +lonee +lonergan +long +long-chu +longbott +longcham +longchun +longdist +longfiel +longhenr +longo +longpre +longsong +longtin +lonhyn +loni +lonidas +lonn +lonna +lonnard +lonneke +lonni +lonnie +lonnman +lonny +lonsdale +loo +loon +loong +loos +looyen +lope +loper +loperena +lopes +lopez +lopiano +lopinski +loponen +loquerci +lora +lora-lee +lorain +loraine +loralee +loralie +loralyn +lorance +lorant +lorcan +lord +loree +loreen +lorelei +lorelle +lorelynn +loren +lorena +lorenc +lorene +lorens +lorenz +lorenza +lorenzen +lorenzo +loreta +loretta +lorettal +lorette +lorfano +lori +loria +lorianna +lorianne +lorie +lorien +lorilee +lorilyn +lorimer +lorin +lorincz +lorinda +lorine +loring +lorint +loris +lorita +lorletha +lorli +lormor +lorna +lorne +loro +lorrain +lorraine +lorrayne +lorrel +lorri +lorrie +lorrin +lorrine +lorry +lortie +lory +loryn +los +losfeld +losier +loso +losses +lotan +lote +lotfalia +lotfi +lothaire +lothar +lothario +lotochin +lott +lotta +lotte +lotti +lottie +lotty +lotz +lou +lou-hell +louann +loucel +loudiadi +louella +lough +loughery +loughran +loughrin +louhelle +louie +louiqa +louis +louis-ph +louis-re +louisa +louise +louisett +louissei +louk +louka +loukas +louladak +lourdes +loutitia +loux +lovas +lovatt +loveday +lovegrov +lovejoy +lovekin +lovelace +loveland +loveless +lovell +lovett +lovin +lovina +lovitt +lowder +lowe +lowell +lowery +lowietje +lowman +lowrance +lowrie +lowry +lowther +loxton +loy +loyd +loydie +loyer +loyola +loyst +loza +lozier +lozinski +lpo +lrc +lrcrich +lrcrtp +lsi +lsiunix +lu +luan +luann +luanne +lubliner +lubna +lubomir +lubomyr +luc +luca +lucais +lucas +lucco +luce +lucente +lucey +lucho +luci +lucia +lucian +luciana +luciani +luciano +lucias +lucie +lucien +lucienne +lucifer +lucila +lucile +lucilia +lucille +lucina +lucinda +lucine +lucio +lucita +lucius +lucking +lucky +lucretia +lucy +ludchen +ludovico +ludovika +ludvig +ludvikse +ludwick +ludwig +lue +luella +luelle +luen +luetchfo +luetke +luettcha +luff +lugsdin +lugwig +luh-maan +luhcs +luhmaan +lui +luigi +luin +luis +luisa +luise +luiza +lujanka +luk +luk-shun +lukas +lukassen +lukaszew +luke +luker +lukers +lukic +lukie +lukshis +lukshun +lula +lulita +lulu +lum +lum-wah +lumley +lumsden +lun +luna +lund +lunde +lundhild +lundstro +lundy +lunk +lunn +luoedora +luoma +luong +lupatin +lupher +lupien +luping +luquire +lura +lurette +luria +lurleen +lurlene +lurline +lusa +luscombe +lussier +luszczek +lutan +lutero +luther +luthin +lutz +luu +luuk +luwemba +luxford +luying +luyten +luz +luzarrag +luzine +ly +ly-khanh +lyall +lychak +lyda +lydda-ju +lydia +lydie +lydon +lyell +lyle +lyliston +lyman +lyn +lynda +lyndal +lynde +lyndel +lyndell +lyndia +lyndon +lyndsay +lyndsey +lyndsie +lyndy +lyne +lynea +lynelle +lynett +lynetta +lynette +lynham +lynn +lynna +lynne +lynnea +lynnell +lynnelle +lynnet +lynnett +lynnette +lynsey +lynton +lynwood +lyon +lyons +lyse +lysinger +lyssa +lystad +lystiuk +lystuik +lysy +lytle +lzrd +maahs +maaike +maala +maan +maarten +maas +mab +mabel +mabelle +mabes +mable +mabson +mabuchi +mac +mac maid +macadams +macalik +macarthu +macaulay +macbride +maccallu +maccarth +macchius +maccombi +macconai +maccorma +macderma +macdonal +macdonel +macdouga +macdowal +macduff +mace +macelwee +macfarla +macgilli +macgrego +mach +machan +machatti +machika +machnick +maciej +maciejew +maciel +macinnes +macinnis +macisaac +maciver +mack +mackay +mackel +mackenzi +mackey +mackin +mackinno +macklem +macklin +mackzum +maclaren +maclauri +maclean +maclella +maclenna +macleod +macmarti +macmeeki +macmilla +macmulli +macnaugh +macneil +macneill +macnicol +macoosh +macphail +macphers +macpost +macquist +macrae +macreyno +macsuppo +mada +madalena +madalene +madalyn +madan +madani +maddalen +maddi +maddie +maddix +maddox +maddy +madel +madelain +madelein +madelena +madelene +madelin +madelina +madeline +madella +madelle +madelon +madelyn +mader +madge +madgett +madhav +madhavan +madhu +madhukar +madigan +madill +madisett +madison +madl +madlen +madlin +mado +madonna +madras +madsen +maduri +mady +mae +maegan +maennlin +maenpaa +maeya +mag +magbee +magda +magdaia +magdale +magdalen +magdi +magdiel +magdy +mage +maged +magee +mages +maggee +maggi +maggie +maggy +maghsood +maginley +maglione +magnan +magnolia +magnum +magnuson +magnusse +magnusso +magrath +magri +mags +maguire +mah +mahaffee +mahala +mahalia +mahaling +mahbeer +mahboob +mahendra +maher +mahesh +maheu +maheux +mahfouz +mahibur +mahiger +mahin +mahlen +mahlig +mahlon +mahmood +mahmoud +mahmud +mahmut +mahn +mahon +mahonen +mahoney +mahoney- +mahshad +mai +maia +maible +maid +maidenhe +maidens +maidisn +maidlab +maidsir +maidxpm +maier +maiga +maighdil +maijala +maika +maikawa +maikhanh +mail +mailroom +mainardi +maine +mainoo +maint +mainvill +mainwari +mair +maire +maisey +maisie +maison +maisonne +maitilde +maitland +maitreya +majd +maje +majeed +majek +majella +majernik +majid +majmudar +major +majors +majumdar +majury +mak +makam +makarand +makarenk +makary +makeda +makiko +makino +makinson +makohoni +makoid +makoto +maksoud +maksuta +mal +mala +malachi +malaclyp +malaher +malaika +malaivon +malam +malani +malanie +malanos +malavia +malaysia +malchy +malcolm +malden +malec +malee +malek +malena +maleski +malethia +malgorza +malgosia +malhi +malhotra +malia +maliepaa +malik +malina +malinda +malinde +malisic +maliski +malissa +malissia +malizia +malkani +malkiewi +malkinso +mallari +malle +mallejac +maller +mallett +mallik +mallissa +mallorie +mallory +malloy +mallozzi +malmqvis +malone +maloney +malorie +malory +malott +malta +maltby +maltese +maludzin +malus +malva +malvin +malvina +malynda +malynne +malynows +malyszka +malzahn +mame +mami +mamie +mamikuni +mamoru +mamoulid +man +man-fai +manageme +manager +manahil +manalac +manami +manas +manavend +manceau +manchest +mancini +mand +manda +mandana +mandayam +mandel +mandevil +mandi +mandie +mandrusi +mandruso +mandy +maneatis +manek +maness +manette +manfred +manfredo +mang +mangione +mangum +manh +manhatte +mani +manica +manickam +manijeh +manilal +manimozh +maninder +manish +manitius +manitsas +manjari +manjeri +manjinde +manjit +manjrike +mankowsk +manley +manli +manly +manmohan +mann +manna +manner +manners +mannie +manning +mannino +mannion +manno +manny +mano +manoel +manoharm +manoj +manol +manolios +manolis +manolo +manon +manou +manouch +manoukia +mansbrid +mansell +manshih +mansi +manson +mansour +mansoura +mansouri +mansukha +mansum +mantell +manto +manuel +manuela +manus +manuszak +manverse +manwarin +manya +mao +maohua +mapes +mapile +mapp +mar +mara +marabel +maracle +maragoud +marano +marasco +marasliy +marc +marc-and +marc-ant +marcanti +marcanto +marce +marceau +marcel +marcela +marcelia +marcelis +marcella +marcelle +marcelli +marcello +marcellu +marcelo +marcey +march +marchall +marchand +marchant +marcheck +marchell +marchese +marchett +marci +marcia +marcie +marcile +marcilie +marcille +marciniu +marcio +marco +marcom +marconi +marcos +marcotte +marcoux +marcum +marcus +marcy +mardi +marea +mareah +marek +marella +maren +marena +marenger +maressa +marette +marg +marga +margalit +margalo +margaret +margarie +margarit +margaux +marge +margeaux +margery +marget +margetso +margette +margheri +margheti +margi +margie +margit +margitta +margo +margot +margret +margriet +margueri +margy +marhta +mari +maria +mariabel +mariaele +mariaisa +mariam +marian +mariana +mariani +mariann +marianna +marianne +maribel +maribell +maribeth +marice +maridel +marie +marie-an +marie-je +marie-jo +marie-lu +marie-na +marieann +mariejea +marieka +marieke +mariel +mariela +mariele +marielle +mariesar +mariet +marietta +mariette +marigold +marijke +marijn +marijo +marika +mariland +marilee +marilin +marillin +marily +marilyn +marilynn +marin +marina +marinaro +marineau +marinett +marinna +marino +marinos +mario +marion +mariotti +mariou +mariquil +maris +marisa +marisca +marisela +mariska +marissa +marit +marita +maritan +maritsa +maritza +marius +mariya +mariza +marj +marja +marjan +marje +marjean +marjet +marji +marjie +marjo +marjoke +marjolei +marjorie +marjory +marjy +mark +markell +markes +marketa +marketin +markham +markiewi +markins +markland +markle +markm +markmeye +marko +markos +markovic +marks +markus +marla +marlaine +marlane +marleah +marleau +marlee +marlee-j +marleejo +marleen +marlena +marlene +marley +marlie +marlies +marlin +marline +marling +marlo +marloes +marlon +marlow +marlowe +marlsela +marlyn +marlyne +marmaduk +marmen +marmillo +marmion +marna +marne +marneris +marney +marni +marnia +marnie +marnix +maroucho +maroun +marples +marquart +marquez +marquita +marr +marra +marrec +marren +marrett +marriet +marrilee +marriott +marris +marrissa +marron +marrone +marryann +mars +marscha +marschew +marsden +marsh +marsha +marshal +marshall +marshaus +marshman +marsie +marsiell +marson +marston +mart +marta +martainn +martel +martell +martelle +marten +martens +martenso +martenst +martguer +martha +marthe +marthena +marti +martica +martie +martijn +martin +martina +martince +martinci +martine +martinea +martinez +marting +martino +martins +martinus +martita +marttine +marturan +marty +martyn +martynne +marui +marum +maruszak +maruszew +marv +marve +marvel +marvell +marven +marvette +marvin +marwan +marwin +marx +mary +mary-ann +mary-ell +mary-jan +mary-jo +mary-mic +mary-pat +marya +maryak +maryam +maryann +maryanna +maryanne +marybell +marybeth +maryelle +maryjane +maryjo +marykate +marykay +maryl +marylee +marylin +marylind +marylou +marylynn +marymarg +maryn +maryrose +marys +marysa +maryse +marysue +maryvonn +marzella +marzullo +masa +masahiro +masako +masales +masamich +masanao +masanobu +masao +masapati +masaru +masciare +masha +mashura +masika +masini +maskell +maskery +maslen +maso +mason +masood +masooda +masotti +masoud +masse +massengi +massey +massicot +massimil +massimo +massinga +masson +massone +massonne +massoud +massoudi +massumi +mastella +mastenbr +masterpl +masters +masterso +mastrang +mastroma +mastrona +masty +mat +mata +mataga +matalon +matatall +matelda +mateo +materkow +materna +mathe +mather +matheson +mathew +mathews +mathewso +mathian +mathias +mathieso +mathieu +mathilda +mathilde +mathis +mathiue +mathur +mathurin +mathus +matias +matibag +matilda +matilde +matlock +matney +matrin +matrundo +mats +matson +matsubar +matsugu +matsunag +matsushi +matsuzak +matsuzaw +matt +matteau +matteo +mattes +matthaeu +mattheus +matthew +matthews +matthias +matthieu +matthiew +matthus +matti +mattias +mattie +mattiuss +mattiuz +matton +mattson +matty +matusik +mau +mau-pin +mauck +maud +maudalis +maude +maudie +maudrie +maudy +mauer +mauldin +maund +mauney +maunu +maupin +maura +maure +maureen +maureene +maurene +maurer +maurice +mauricio +maurie +maurijn +maurine +maurino +maurise +maurita +maurits +maurizia +maurizio +mauro +maury +maveety +mavis +mavra +mavrou +mawani +mawji +mawst +max +maxey +maxi +maxie +maxim +maximili +maximo +maxin +maxine +maxseine +maxsom +maxwell +maxy +may +maya +mayaram +mayasand +maybee +maybelle +maycel +maycock +maye +mayea +mayenbur +mayer +mayes +mayeul +mayfield +mayhugh +mayi +maylynn +mayman +maynard +mayne +maynes +maynie +maynord +mayo +mayor +mayoux +mayr +mayr-ste +mays +mayumi +mayya +mazahir +mazanji +mazarick +mazen +mazey +mazlack +mazurek +mazyar +mazzei +mbrose +mc +mc alpin +mc ginn +mcadam +mcadams +mcadorey +mcafee +mcalear +mcaleer +mcaliste +mcallist +mcallum +mcandrew +mcarthur +mcateer +mcaulay +mcauliff +mcbeth +mcbrayne +mcbride +mcbroom +mcbryan +mccabe +mccaffer +mccaffit +mccaffre +mccaig +mccain +mccall +mccalla +mccallen +mccallum +mccampbe +mccandle +mccann +mccarrel +mccarrol +mccarron +mccarthy +mccartin +mccartne +mccarty +mccaughe +mccauley +mccaw +mcclain +mcclarre +mcclary +mcclean +mccleery +mcclella +mcclendo +mcclenno +mcclinto +mccloske +mcclough +mcclure +mccluske +mcclymon +mccoll +mccollam +mccollum +mccolman +mccomb +mccombs +mcconagh +mcconkey +mcconnel +mcconney +mccord +mccorkel +mccorkle +mccormac +mccormic +mccorquo +mccoy +mccoy-ca +mccracke +mccrain +mccraney +mccray +mccready +mccreano +mccrear +mccreath +mccreesh +mccrimmo +mccuaig +mccue +mccullen +mcculloc +mccullog +mccullou +mccully +mccune +mccurdy +mccusker +mcdade +mcdaniel +mcdavitt +mcdermot +mcdevitt +mcdonald +mcdonnel +mcdonoug +mcdoom +mcdougal +mcdowall +mcdowell +mcduffie +mcdunn +mceacher +mcelderr +mcelhone +mcelligo +mcellist +mcelrea +mcelroy +mcevoy +mcewan +mcewen +mcewen-r +mcfadden +mcfall +mcfarlan +mcfeely +mcgallia +mcgarry +mcgaughe +mcgee +mcgehee +mcgeown +mcghee +mcgill +mcgillic +mcgillvr +mcgilly +mcginley +mcginn +mcglynn +mcgonagl +mcgoniga +mcgorman +mcgough +mcgovern +mcgowan +mcgracha +mcgrath +mcgregor +mcgruder +mcguigan +mcguinne +mcguire +mcgurn +mchale +mchan +mcharg +mchugh +mcilrath +mcilroy +mcinerne +mcinnis +mcintee +mcintire +mcintomn +mcintosh +mcintyre +mcisaac +mciver +mckay +mckeage +mckeague +mckearne +mckechni +mckee +mckeegan +mckeen +mckeigha +mckeitha +mckenna +mckenney +mckenzie +mckeone +mckeown +mckerrow +mckibben +mckibbin +mckibbon +mckie +mckillop +mckinlay +mckinley +mckinney +mckinnon +mcknelly +mcknight +mckusick +mclachla +mclaren +mclauchl +mclaughl +mclawhon +mclawhor +mclean +mclellan +mclemore +mclenagh +mclendon +mclennan +mcleod +mcluskie +mcmahan +mcmahon +mcmanis +mcmann +mcmannen +mcmanus +mcmaster +mcmeegan +mcmenami +mcmichae +mcmillan +mcmillen +mcmillia +mcmillio +mcminn +mcmonagl +mcmullen +mcmullin +mcmurray +mcnab +mcnabb +mcnair +mcnally +mcnamara +mcnamee +mcnaught +mcneal +mcnealy +mcneely +mcneese +mcneil +mcneill +mcneilly +mcnerlan +mcnerney +mcnichol +mcnicol +mcniel +mcnitt +mcnulty +mcphaden +mcphail +mcphee +mcpherso +mcquaid +mcquaig +mcquarri +mcqueen +mcrae +mcrann +mcready +mcritchi +mcronald +mcruvie +mcshane +mcsheffr +mcsorley +mcsween +mctaggar +mctavish +mctierna +mcturner +mcvay +mcveety +mcveigh +mcvey +mcvicar +mcvicker +mcwalter +mcwaters +mcwherte +mcwhinne +mcwhorte +mcwilton +mdhazali +mduduzi +me +meachum +mead +meade +meadows +meads +meagan +meaghan +meagher +mealin +meany +meara +measures +mebine +mechael +meche +mechelle +mecher +meckler +meckley +mecteau +medefess +medeiros +medel +meder +medill +medioni +medlin +medlock +mednick +medria +meehan +meeks +meena +meenaksh +meer +meera +meerveld +meese +meeting +meffe +meg +megan +megen +meggi +meggie +meggitt +meggo +meggy +meghan +meghani +meghann +megumi +mehboob +mehd +mehdi +mehelis +mehetabe +mehlhaff +mehmet +mehmud +mehrdad +mehrez +mehrzad +mehta +mehul +mei +mei-jywa +meier +meijer +meijywan +meikle +meilleur +mein +meining +meir +meiser +meisner +meissner +mejdal +mejia +mejury +mel +mela +melamie +melana +melani +melania +melanie +melanson +melantha +melany +melaura +melba +melberta +melbourn +meldia +meldrum +melecio +meleg +melek +melesa +meleski +meleskie +melessa +meletios +melfi +melford +melhem +meli +melicent +melina +melinda +melinde +melinie +melisa +melisand +melisend +melisent +melissa +melisse +melita +melitta +melkild +mella +melli +mellicen +mellie +mellisa +mellisen +mello +melloney +mellor +melly +melnyk +melodee +melodie +melody +meloling +meloney +melonie +melony +melosa +melton +melucci +melva +melvin +melvina +melvyn +melynda +men-kae +menaker +menard +menasce +menashi +menashia +mencer +mendel +mendelso +mendez +mendie +mendolia +mendonca +mendorf +mendoza +mendy +menechia +menendez +meng +mengly +menkae +menke +menna +mennie +menon +menqiong +mensch +mensinka +mentor +menyhart +menzel +menzies +mer +merat +merb +mercedes +mercer +merci +mercie +mercier +mercilin +mercy +merdia +meredeth +meredith +merell +merelyn +meres +mereu +meriann +meridel +meridew +meridian +meridith +meriel +merilee +meriline +merill +merilyn +merinder +meris +merissa +meriweth +merizzi +merkling +merl +merla +merle +merlin +merlina +merline +mermelst +merna +merola +merralee +merrel +merrett +merrick +merridie +merrie +merriell +merrile +merrilee +merrili +merrill +merrills +merrils +merrily +merrithe +merritt +merry +merryman +merrywea +mersch +mersey +mersinge +merton +merunix +merv +mervin +mervyn +merwin +merworth +merwyn +meryl +merylene +meseberg +mesirov +meskimen +mesko +mesquita +message +messer +messeria +messersc +messick +messier +messinge +mesut +meszaros +meta +metcalf +metcalfe +metelski +meter +metheny +metherel +methi +methiwal +methot +methul +metin +metler +metraile +metrics +mettrey +metyn +metz +metzger +metzler +meubus +meunier +mevis +mewa +meyer +meyerink +meyers +meza +mezzano +mezzoius +mfgeng +mgmt +mia +miao +miasek +mic +micaela +micah +micahel +miceli +micgael +micha +michael +michael- +michaela +michaeli +michaell +michaels +michaez +michail +michailo +michal +michale +michalos +michaud +micheal +micheil +michel +michele +michelin +michell +michella +michelle +michels +michelse +michelso +michelus +michi +michial +michie +michiel +michigan +michihir +michiko +michiya +mick +mickens +mickey +micki +mickie +micklos +micky +mico +micro +microfab +micucci +mid +middlebr +middleto +midge +midha +miek +mielke +miello +miep +miernik +miers +mierwa +mietek +miezitis +migdalia +mignault +mignon +mignonne +miguel +miguela +miguelit +mihaela +mihai +mihail +mihan +mihara +mihir +mihm +miho +mihran +mika +mikael +mikaela +mike +mikel +mikelis +mikeloni +mikey +mikhail +miki +mikie +mikihito +mikkel +miklos +miko +mikol +miksik +mikulka +mil +mila +milaknis +milakovi +milan +milanfar +milanovi +milar +milary +milburn +mildred +mildrid +mile +milena +miles +milford +milian +milicent +milind +milinkov +milissen +milka +mill +millaire +millar +millard +millen +miller +millero +millerwo +millette +milletti +milli +millicen +millie +milligan +millisen +millo +milloy +mills +millspau +millward +milly +milman +milmine +milne +milner +milo +milor +milotte +milou +milstead +milston +milt +miltenbu +miltie +milton +miltos +milty +milutino +milway +milzie +mim +mimi +mims +min +min-jho +mina +minai +minakata +minako +minami +minard +minas +mincey +minck +minda +mindy +minegish +miner +minerva +minesh +minetola +minetta +minette +ming +ming-cha +ming-chu +ming-hue +ming-min +ming-pin +ming-tzo +ming-yua +mingchu +minghuei +minghwan +mingpin +mingtzon +mingyuan +minh +minh-phu +minhwi +minichil +minjho +minkus +minna +minnamin +minne +minnesot +minni +minniche +minnie +minnnie +minny +minor +minority +minoru +minos +minshall +minsky +minta +minter +minthorn +minyard +minzhu +miodrag +miof mel +mior +miotla +mips +miquela +miquette +mir +mira +mirabel +mirabell +miran +miranda +mirande +mirarchi +mireiell +mireille +mirek +mirella +mirelle +mirenda +miriam +miriamne +mirian +mirilla +mirin +mirjam +mirko +mirna +miro +miron +miroslav +miroslaw +mirza +mis +misbah +mischa +misczak +misha +mishina +misium +miskelly +misko +misra +miss +missagh +missaili +missie +misslitz +missy +mister +misti +mistry +mistulof +misty +misutka +mitalas +mitch +mitchael +mitchel +mitchell +mitchels +mitchler +mitesh +mitra +mitrani +mitrou +mitsui +mitsuko +mitsuru +mitsuter +mittleid +mitzi +mivehchi +miwa +miwako +miyuki +mizerk +mkpwent +mkt +mlacak +mlcoch +mmail +mmdf +mnason +mo +moataz +moazzami +mobasher +mobley +mocock +modaffer +modesta +modestia +modestin +modestos +modestou +modesty +modh +modi +modigh +modl +modotto +modula-2 +modula2 +moe +moebes +moel +moen +moening +moeschet +moetteli +moffatt +moffet +moffett +mofina +moghe +moghis +mogridge +moh +moh'd +mohajeri +mohamad +mohamed +mohammad +mohammed +mohan +mohandas +mohandis +moharram +mohd +mohideen +mohler +mohr +mohrmann +mohsen +moina +moira +moise +moises +moishe +moiz +moizer +moja +mojgani +mojtaba +mok +mok-fung +mokbel +mokhtar +mokros +molani +moledina +moleski +moll +mollee +mollerus +molli +mollie +molloy +molly +molnar +molochko +moloney +molson +molyneux +mombourq +mommy +momon +momtahan +mona +monachel +monaco +monaghan +monah +monahan +monardo +moncef +moncion +monck +moncur +mondher +mondor +monet +monette +moneypen +monforto +monfre +mong +mong-tse +mongo +mongtsen +moni +monica +moniek +monika +monino +monique +moniter +monling +monn +monro +monroe +monson +montag +montague +montaldo +montanez +montange +montanin +montcalm +monte +monteene +monteggi +monteiro +montelli +montero +monteros +montgome +monti +montijo +montmore +montor +montoute +montoya +montreal +montreui +montsion +montsko +montuno +monty +mony +monzo +moo-youn +mooder +moogk +mooken +moomey +moon +moondog +mooney +moonistt +moorcrof +moore +moore-vi +moorefie +moorer +moores +moorhous +moosavi +mora +moraetes +morais +morales +moran +moray +morcinel +mord +mordecai +mordecha +morden +mordy +morearty +moreau +moree +moreen +morehead +morek +moreland +morelli +morena +moreno +moreton +morettin +morey +morgan +morgan-c +morgana +morganic +morganne +morgen +morglan +morgun +mori +moria +moriarty +morie +morimoto +morin +morini +morino +moris +morishig +morissa +morisset +moritz +moriyama +morlee +morley +morly +morna +morneau +morocz +moroney +moroz +morra +morreale +morrie +morrin +morris +morrison +morrisse +morry +morse +morson +mort +morten +morteza +mortie +mortimer +morton +morty +moschopo +mose +moseby +moselle +moser +moses +mosetta +moshe +moshinsk +moshiri +moshtagh +moshy +mosis +moskalik +mosley +moss +mostafa +mostovac +mosur +mot +motashaw +motasim +motaz +mote +motoko +mototsun +mott +motwani +mou +moua +mouat +moubarak +mougy +mouillau +moulds +moulsoff +moulton +mouna +mounir +mountfor +moussa +mousseau +moussett +moveline +movies +mowat +mowbray +mowle +moxham +moxley +moxon +moy +moya +moyano +moyce +moyenda +moyer +moyers +moyes +moyna +moynihan +moyoli +moyra +mozart +mozek +mozelesk +mozelle +mozes +mraz +mrozinsk +mrugesh +msg +mshia +mtcbase +mtl +mtlipadm +mtnview +mu-cheng +mucci +mucheng +muchow +mucklow +mudd +mudge +mudie +mudry +muehle +mueller +muenster +muffin +mufi +mufinell +mufti +mugniot +muh-cher +muhammad +muhammed +muhchern +mui +muinck +muir +muire +muise +mujahed +mukai +mukhar +mukharji +mukherje +mukhopad +mukul +mukund +mulder +mulders +muldoon +mulero +mulherka +mulholla +mullaly +mullaney +mullarne +mullen +muller +mullett +mullin +mullinix +mullins +mulmuley +mulot +mulqueen +mulroney +multispe +mulumba +mulvie +mumford +mumma +mummy-cr +mun-choo +mun-hang +munaz +munchoon +munden +mundi +mundy +muni +munikoti +munir +muniz +munjal +munmro +munn +munna +munns +munro +munroe +munsey +munson +munter +munz +munzer +muqarrab +muradia +muralidh +murash +murat +murawski +murchiso +murdaugh +murdeshw +murdoch +murdock +mureil +murial +muriel +murielle +murison +murnagha +muro +murock +murph +murphin +murphy +murphy-k +murray +murrell +murrill +murrin +murris +murry +murson +murtagh +murtaza +murthy +murton +murty +murveit +murvyn +musa +musca +musclow +muselik +musen +musgrove +musick +mussalle +mussar +musselwh +musser +mustafa +mustapha +mustillo +mutcher +muth +muthuswa +mutikain +mutsuo +muttaqi +muus +muzio +muzz +mwa +my +mya +myatt +myca +mycah +mychal +myer +myers +myers-pi +myhill +mykitysh +myla +myles +mylne +mylo +mymryk +myong +myoung +myra +myrah +myranda +myre +myriam +myrick +myrilla +myrillas +myrle +myrlene +myrna +myron +myroon +myrta +myrthill +myrtia +myrtice +myrtie +myrtille +myrtle +myrvyn +myrwyn +mysore +mystkows +myung +myunghee +myungho +nabeel +nabeil +nabil +nabisco +nabors +nace +nachtshe +nachum +nad +nada +nadav +nadean +nadeau +nadeau-d +nadeem +nadeen +nadel +nader +nadezhda +nadi +nadia +nadim +nadine +nadir +nadiya +nadler +nadolny +nadon +nady +nadya +nae-ming +naem +naeming +nafezi +nafsika +nagai +nagaraj +nagaraja +nagaratn +nagarur +nagel +nagendra +nagenthi +nagle +naguib +nagy +nagys +nahabedi +nahas +nahata +nahid +nahmias +nahornia +nahum +naile +naim +naima +naimpall +nair +nairn +naismith +najafi +naji +najib +nakagome +nakamura +nakano +nakatsu +nakhla +nakhoul +nakina +nakonecz +nalani +naldrett +nalin +nall +nallenga +nam +nam-kiet +nam-soo +namasiva +nambride +namdar +nami +namiki +namont +namrata +nan +nana +nanamiya +nananne +nance +nancee +nancey +nanci +nancie +nancy +nancyjea +nanda +nandakum +nandan +nandi +nandita +nando +nanete +nanette +nang +nani +nanice +nanine +nannette +nanni +nannie +nanny +nanon +naohiko +naoko +naolu +naoma +naomi +naor +naoto +naoum +nap +naparst +napert +naphan +naphtali +napier-w +napke +napoleon +napper +nappie +nappy +nara +narasimh +narayan +narayana +narciso +narda +nardiell +naren +narendra +naresh +nari +narida +nariko +narinder +naro +narraway +narrima +narron +narsimha +nasato +naser +nash +nashib +nashif +nashir +nashvill +nasir +nason +nass +nasser +nassoy +nassr +nasvin +nat +nata +natal +natala +natale +natalee +natalia +natalie +natalina +nataline +nataliya +nataly +natalya +nataniel +nataraja +natascha +natasha +natasja +natassia +natassja +natchez +nate +nath +nathalia +nathalie +nathan +nathanae +nathania +nathanie +nathanil +nathoo +national +natiuk +nativida +natka +natkin +natraj +natty +natver +natvidad +naufal +naugle +naujokas +naujoks +nault +nava +naval +navalta +navaratn +navarre +navarro +naveda +naveed +naveen +navid +navneet +nawa +nawaby +nayan +naybor +naylor +nayman +nayneshk +nayyer +nazanin +nazardad +nazeh +nazi +nazib +nazli +nazman +ncc +ndububa +ne-regio +neal +neala +neale +neall +nealon +nealson +nealy +neamtu +neander +nearing +nearyrat +neate +neault +nebel +ned +neda +nedda +nedderma +neddie +neddy +nedi +needham +neefs +neel +neely +neena +neene +neeraj +neetu +nefen +neff +negandhi +negar +neghabat +negrich +nehemiah +nehring +neibauer +neider +neidy +neifert +neil +neila +neile +neill +neilla +neille +neilly +neils +neilsen +neilson +neisius +neitzel +nekueey +nel +nelda +nelia +nelie +nelken +nell +nelle +nelleke +nelli +nellie +nellis +nelly +nelon +nels +nelsen +nelson +nemec +nemes +nemeth +nemirovs +nenad +neng-chu +nengchun +nentwich +neoh +nerby +nereida +nerem +nerissa +nerita +nermana +nero +neron +nert +nerta +nerte +nerti +nertie +nerty +nesbitt +nesralla +ness +nessa +nessi +nessie +nessman +nessy +nesta +neste +nester +nestor +netas +netdbs +netdev +netherso +netick +netlink +neto +netta +netteam +netti +nettie +nettle +nettles +netto +netty +network- +networkr +networks +netzke +neubauer +neudeck +neufeld +neuman +neumann +neumeist +neureuth +neuschwa +neustift +neusy +nev +neva +nevardau +nevein +nevil +nevile +neville +nevin +nevins +nevison +nevrela +nevsa +new +newberry +newbold +newby +newcomb +newcombe +newell +newham +newhook +newkirk +newlab +newland +newman +newnam +newport +news +newsom +newsome +newton +neyer +neyman +neyra +neysa +neywick +nezm +nezon +ng +nga +ngai +ngai-nga +ngaingai +ngan +nghia +ngina +ngo +ngoc +ngocquyn +nguy +nguyen +nguyen-t +nguyet +nha +nhan +nhat +nhien +nhut +nial +niall +niamh +nic +nicandro +nicas +niccolls +niccolo +nichael +nichol +nicholas +nichole +nicholl +nicholle +nichols +nicholso +nici +nick +nickell +nickells +nickels +nickerso +nickey +nicki +nickie +nickle +nicklin +nicko +nickola +nickolai +nickolas +nickolau +nickonov +nicky +nico +nicol +nicola +nicolai +nicolais +nicolaou +nicolas +nicole +nicolea +nicoles +nicolett +nicoli +nicolina +nicoline +nicolis +nicolle +nicolo +nicolopu +nicosia +nie +niebudek +niedelma +niedens +niedra +niedzwie +niek +niel +nield +niels +nielsen +nielson +niemi +nien +nien-hwa +nienhwa +niepmann +niepokuj +niergart +nigam +nigel +nihar +nijen +nik +nikaniki +nike +nikfarja +niki +nikifori +nikiforu +nikita +nikki +nikkie +nikky +niklas +niko +nikola +nikolai +nikolaos +nikolas +nikolaus +nikolett +nikolia +nikolopo +nikolos +nikos +nil +nilakant +niles +nilesh +nill +niloofar +nils +nilson +nilsson +nima +nimesh +nimish +nimmo +nimr +nimzod +nina +ninetta +ninette +ninety-o +ning +ninja +ninnetta +ninnette +ninno +ninon +nipper +nir +niraj +niranjan +nirmal +niro +nirwan +nisa +nisan +nisbet +nischuk +nishan +nishida +nishiguc +nishihar +nishimot +nishimur +nishioka +nishith +nishiwak +nishiyam +nishizak +nissa +nisse +nissie +nissy +nita +nital +nitin +nitschky +nitza +nitzhe +nitzhye +niu +niven +nix +nixie +nixon +nixxon +niz +nizam +nizamudd +nizar +nizman +nj +njo +nnamdi +nnamudi +no +noach +noah +noak +noam +noami +nobe +nobel +nobes +nobie +noble +nobuko +nobuo +nobutaka +nobuto +nobuyuki +noby +nock +noddin +node +noe +noel +noelani +noell +noella +noelle +noellyn +noelyn +noemi +noeschka +noffke +noguchi +nokes +nola +nolan +nolan-mo +nolana +noland +nolen +noles +nolet +nolie +noll +nollie +nolly +nolter +nomi +nomura +nona +nonah +noname +nong +nongqian +noni +nonie +nonkes +nonna +nonnah +noone +noorani +noorbehe +noorbhai +nooshin +nopi +nora +norah +noralie +noraly +norbert +norberto +norbie +norby +norcal +norczen +nordskog +nordstro +norean +noreen +norel +norena +norene +norfleet +norgaard +norikats +norikazu +noriko +norina +norine +norio +noris +norm +norma +norman +normand +normandi +norment +normie +normy +norndon +noronha +norri +norrie +norris +norry +norstar +north +northam +northcot +northrop +northrup +norton +norval +norvie +norvig +norwood +nosewort +noslab +nosov +nostrada +notley +nou +noubar +nouira +noujeim +nour +nouri +nova +novak +novelia +novene +novia +novisedl +novo +novorols +novotny +nowak +nowell +nowina-k +nowlin +noy +noye +noyes +npi +nss +ntelpac +ntinash +ntlc +ntpadmin +ntprel +nttest +nuber +nuetzi +nugent +number +nunes +nunez +nung +nunn +nunnally +nuno +nunold +nuntel +nurettin +nurhan +nuri +nuria +nurly +nurmi +nuttall +nuvit +nyaguthi +nyberg +nyce +nydia +nye +nyeita +nyenhuis +nyland +nyquist +nyre +nys +nyssa +o karina +o'brecht +o'brian +o'brien +o'carrol +o'colmai +o'connel +o'conner +o'connor +o'dacre +o'dale +o'dea +o'dell +o'dohert +o'donnel +o'donova +o'dwyer +o'farrel +o'grady +o'hagan +o'hara +o'hearn +o'heocha +o'higgin +o'keefe +o'keeffe +o'kelly +o'leary +o'malley +o'meara +o'murchu +o'neal +o'neall +o'neil +o'neill +o'regan +o'reilly +o'rourke +o'shaugh +o'shea +o'sulliv +o'toole +o_kelly +oakland +oakley +oaks +oam +oanes +oanh +oastler +oates +oaul +obadiah +obadias +obed +obeda +obediah +obeidat +obenauf +ober +oberhamm +obermeie +obermeye +obermyer +oberon +oberpril +obidiah +obie +oblak +obrecht +obrien +obrusnia +oby +ocampo +ochman +ochoa +ochs +ocone +oconnor +octavia +octavio +octavius +odac +odden +ode +odecki +oded +odegaard +odele +odelia +odelinda +odell +odella +odelle +oden +odessa +odetta +odette +odey +odgers +odie +odile +odilia +odille +odo +odum +ody +oedipal +oedipus +oertelt +oesterre +oestreic +oetting +oey +ofcparm +ofcparms +ofelia +ofella +ofer +offers +ofilia +ogan +ogborn +ogburn +ogdan +ogden +ogdon +ogilvie +oglesby +ogrodnik +ogua +oguz +ohala +ohandley +ohara +ohare +ohashi +ohio +ohmaru +ohmayer +ohn +ohsone +oingres +oivind +ojala +ojerholm +oka +okada +okafo +okai +okamoto +okan +okay +oke +okey +okon +okrafka +oksana +okseniuk +oktar +okun +okura +okuyama +okuzawa +ola +olag +olav +old +older +oldfield +oldham +oldright +ole +oleesa +oleksysh +olenka +olenolin +olesen +olesko +olga +olia +olimpia +olin +olinger +olinyk +olivares +olive +oliveira +oliver +olivero +olivette +olivia +olivie +olivier +oliviero +oliy +olken +ollie +olliff +olly +olmstead +olness +olof +olsen +olsheski +olson +olszewsk +olusola +olva +olvan +olwen +olympe +olympia +olympie +olynyk +oma +oman +omar +omayma +omer +omerine +omero +omid +omori +omura +omyeabor +onassis +onder +ondovcik +ondrea +oneida +oneto +onette +onfre +onfroi +ong +onge +onida +onofredo +onsitete +onsy +onufrak- +onyshko +ooi +oona +oorschot +oost +op +opal +opalina +opaline +opalski +open +oper +operatio +operator +ophelia +ophelie +oplinger +opperman +ops +opsplng +optimiza +opto +opus +ora +oral +oralee +oralia +oralie +oralla +oralle +oran +orazio +orbadiah +orca +ord +ordas +orden +orders +ordog +ordway +oreffice +oreilly +orel +orelee +orelia +orelie +orella +orelle +oren +orenzo +orfano +orford +organiza +orgren-s +oriana +orie +orin +orion +orla +orlan +orland +orlando +orly +orlyn +orme +ormesher +ormsby +ornburn +ornelas +orol +oros +orpheus +orr +orran +orren +orrin +orsa +orser +orsini +orsola +orson +ortensia +orth +ortiz +orton +orv +orville +orvin +orwell +oryal +osada +osadciw +osama +osami +osatuik +osbert +osborn +osborne +osbourn +osbourne +osburn +oscar +osgood +oshinski +oshiro +osiakwan +osiris +oskar +oskorep +oslund +osman +osmond +osmund +osofsky +ossama +ossie +ostapiw +ostarell +ostaszew +oster +osterber +osterhou +osterman +ostifich +osvaldo +oswald +oswalt +oswell +otakar +otani +otec +otes +otfried +otha +othelia +othella +othello +othilia +othilie +otho +othon +otis +otmar +otsuka +ott +ottawa +ottco +ottcsr +otter +ottilie +otto +ottoman +ottosson +ottowa +oturakli +otway +oucharek +oue +ouellet +ouellett +ouimet +ousterho +outage +outhwait +outram +ouzas +ovans +ovas +overby +overcash +overdyke +overton +oviedo +ovila +owen +owens +owensby +owsiak +oxendine +oyama +oyung +ozalp +ozan +ozay +ozer +ozersky +oziemblo +oziskend +ozkan +ozlem +ozmizrak +ozmore +ozselcuk +ozyetis +ozzie +ozzy +paar +pablo +pac +pace +pacey +pachal +pacheco +pachek +pachner +pachulsk +pacific +packager +packard +packston +paco +pacon +pacorro +paczek +paczynsk +paddie +paddon +paddy +paden +padget +padgett +padiath +padilla +padiou +padma +padmanab +padraic +padraig +padriac +paerio +paes +paetsch +pafilis +pagani +page +pageau +paget +pagi +paglia +pagliaru +pai +paialung +paige +paik +pail +paine +painter +painters +pak +pak-jong +pak-kin +pakkin +paksi +pakulski +pal +palacek +palamar +palasek +palczuk +palermo +paley +palfreym +palidwor +paliga +palik +paliwal +pall +pallab +pallen +palm +palme +palmer +paloma +palomar +paluso +pam +pambianc +pamela +pamelina +pamella +pammi +pammie +pammy +pamperin +pan +panacea +panagiot +pancewic +panch +panchen +panchito +panchmat +pancho +pancholy +pandey +pandolfo +pandora +pandrang +pandya +panek +panesar +pang-chu +pangchun +panger +pangia +panizzi +pankaj +panke +pankesh +pankhurs +pankiw +panko +pankratz +pannell +panolil +panos +panosh +pansie +pansy +pantages +pantalon +pantas +pantelis +panter +panton +panzer +pao +pao-ta +paola +paoletti +paolina +paolo +paone +paota +papa +papadopu +papagena +papageno +papageor +papahadj +papajani +papalits +papanton +paparell +pape +paper +paperno +papers +papiez +papineau +papp +pappas +papper +pappu +paprocki +paqs +paquette +paquin +paquito +par +para +paracha +paradis +paradise +parasili +pardeep +pardi +pardip +pardo +parekh +paresh +parham +parhi +parichay +parihar +parikh +paris +parise +parisen +parisi +parisien +park +parkash +parke +parker +parker-s +parkes +parkhill +parkin +parkins +parkinso +parks +parkson +parlett +parmakse +parman +parmar +parmente +parmigia +parminde +parn +parnas +parnell +parniani +parnigon +parow +parr +parra +parrilli +parrillo +parris +parrish +parrish- +parrnell +parrott +parry +pars +parsifal +parsloe +parsons +part +partap +partello +partha +parthasa +partick +partin +partlo +parton +partovi +paruleka +parveen +parvin +parviz +paryag +parypa +pas +pascael +pascal +pascale +pascali +pascas +paschall +pasher +pashia +pashmine +pasiedb +pasquale +passier +passin +pasterna +pastore +pastorek +pastuszo +pasvar +pat +patacki +patadm +patch +patchcor +patches +patchett +patching +patchit +patchor +patchsqa +patcor +pate +patel +paten +patenaud +paterson +patey +pathak +patience +patin +patner +pato +patoka +paton +patoskie +patra +patriarc +patric +patrica +patrice +patrice- +patricem +patrici +patricia +patricio +patrick +patrizia +patrizio +patriziu +patry +patsy +patt +patte +patten +patterso +patteson +patti +pattie +pattin +pattison +patton +pattra +pattullo +patty +patwardh +pau +paul +paula +paulas +paule +paulett +pauletta +paulette +pauley +paulhus +pauli +paulich +paulie +paulien +paulin +paulina +pauline +pauling +paulinus +paulita +paulk +paulo +paulovic +paulus +pauly +paunins +pautenis +pavan +pavel +pavia +pavitt +pavla +pavlic +pavlov +pavlovic +pawel +pawelchu +pawlikow +pawliw +paxon +paxton +paye +payette +paylor +payn +payne +paynter +payroll +payton +pazos +pbkim +pbx +pcboards +pcbtools +pcsuppor +pcta +pde +pdesuppo +peabody +peacemak +peach +peacocke +peadar +peake +pearce +pearcy +pearl +pearla +pearle +pearline +pearse +pearson +peart +peate +peaugh +peavoy +pebrook +pecic +peckel +peckett +peder +pederson +pedigo +pedley +pedneaul +pedram +pedriana +pedro +peebles +peedin +peerman +peers +peeters +peets +peg +pegasus +pegeen +peggi +peggie +peggy +pegler +pehong +pei-chie +pei-ling +peiling +peirce +peiser +peixoto +peleato +pelegri +pelissie +pelkie +pell +pelland +pellegri +pelletie +pellizza +pellizze +pelly +pelosi +pelot +pelton +peluso +pelz +pembroke +pen +pen-mi +pen-min +pena +pena-fer +penang +pendergr +pendhark +pendleto +penelopa +penelope +penfield +peng +peng-dav +penland +penmi +penmin +penn +pennebak +pennell +penner +penney +penni +pennie +penninge +penningt +penny +penrod +penrose +pension +peon +peoples +pepc +pepe +pepi +pepillo +pepin +pepita +pepito +pepler +pepper +pepple +peptis +per +pera +peralta +perazzin +perceval +perchtho +percival +percy +peregrin +pereira +perenyi +perez +perfetti +peri +peria +pericak +perice +pericles +perina +perkin +perkins +perkinso +perl +perla +perle +perleber +perlmutt +pernell +perng +perona +peroxra +perras +perrault +perreaul +perrella +perren +perri +perrier +perrin +perrine +perron +perrotta +perry +perryman +perryno +persaud +perschke +persechi +pershing +persis +personna +peschke +pesik +pesold +pestill +pet +peta +petar +pete +peter +peterman +peters +petersen +peterson +peterus +petey +petillio +petr +petra +petrakia +petras +petre +petrea +petree +petrescu +petretta +petrey +petri +petrick +petrie +petrina +petrinac +petro +petronel +petronia +petronil +petrovic +petruck +petrunew +petrunka +petschen +petter +pettinge +pettitt +petunia +petzold +pevec +pevzner +pewitt +pey-kee +peyman +peyter +peyton +pezzoli +pezzoni +pezzullo +pfeffer +pfeilsch +pfieffer +pfifferl +pfitzner +pflughau +phaedra +phagan +phaidra +phair +phal +phalen +phalpher +pham +phan +pharr +pharris +phat +phebe +phedra +phelan +phelia +phelps +phifer +phil +philbeck +philbert +philion +philip +philipa +philippa +philippe +philippi +philippo +philips +philis +phill +phillida +phillie +phillip +phillipe +phillipp +phillips +phillis +philly +philomen +philp +phineas +phip +phipps +phiroze +phoebe +phoenix +phonenet +phong +phoung +phu +phuc +phung +phuoc +phuong +phuong-l +phuongli +phyl +phylis +phyllida +phyllis +phyllys +phylys +physical +pi-yu +pia +piasecki +piatt +pic +picard +piche +pichocki +pick +pickens +pickett +pickles +piecaiti +piecowye +piel +pien +pier +piercarl +pierce +piercey +piercy +pierette +piero +pieron +pierosar +pieroway +pierre +pierre-a +pierre-h +pierre-m +pierre-y +pierret +pierrett +pierrick +piersol +pierson +piete +pieter +pietra +pietrek +pietro +pietromo +pietropa +pietrzak +piette +pifko +piggott +piggy +pighin +pigniczk +piitz +pilar +pilch +pilcher +pilip +pilipchu +pilkingt +pillars +pillman +pillswor +pilmoor +pilon +pilot +pilote +pilotte +piltz +pim +pimentel +pimisker +pimpare +pimsiree +pinakin +pinalez +pinar +pincas +pinchas +pincheir +pinchen +pincus +pinder +pindur +pineau +pinecres +pineda +pinel +ping +ping-cha +ping-kon +ping-she +pingchar +pingkai +pingshen +pinizzot +pinkerto +pinnegar +pinney +pino +pinren +pinsonne +pintado +pinto-lo +pintwala +piotr +piotto +pip +piper +piperni +piperno +pipit +pipkins +pippa +pippert +pippin +pippo +pipponzi +pippy +piqueras +piraino +pircher +pires +pirkey +pirkle +pirolli +pirooz +piroska +pirzada +pisani +pisheng +piske +pissot +pister +pistilli +pit +pitawas +pitcairn +pitcavag +pitcher +pitre +pitt +pittam +pittges +pittman +pittner +pitton +pitts +pittsbur +pituley +pivert +piwkowsk +pixie +piyasena +piyathad +piyu +piyush +pizzanel +pizzarel +pizzimen +pkdcd +pkg +placido +plaic +plaisanc +plaisant +plambeck +plamondo +planas +planche +plantamu +plante +planthar +planting +plaskie +plasse +plastic +plastina +plater-z +plato +platt +platthy +platts +playatun +please d +plenderl +plett +plevyak +pleydon +plmcoop +ploeg +ploof +plotter +plouffe +plourde +plsntp +plssup +plucinsk +plummer +pluto +plyler +po +po-rong +po-yi +podlesna +podmarof +podolski +poe +poettcke +poff +poh-soon +pohlmann +poincare +poindext +pointner +poirier +poissant +poisson +pojanart +pokinko +pokrifca +pokrywa +pokusay +polak +polakows +polanco +polashoc +polder +poldi +poleretz +poley +poliwoda +polk +polla +pollack +pollard +pollie +pollinzi +pollux +polly +pollyann +pols +polsha +polson +poluchow +polulack +pom +poma +pomerlea +pomeroy +pommainv +pompeo +pomposel +pon +ponamgi +ponthieu +pontus +poobah +pooh +pookie +poole +poon +poorman +popa +popadick +popcorn +popel +popela +popescu +popierai +popoff +popovich +popovics +popowicz +popowycz +popp +poppa +popper +poppy +porebski +porecha +porfirio +porong +port +portelan +porter +porterfi +portia +portie +portigal +porting +portis +portwood +porty +portz +pory +posavad +poseidon +poshiu +pospisil +posta +postavsk +posthumu +postleth +postolek +potamian +potesta +potkonja +potocki +potter +pottle +potts +potvin +pouhyet +poul +poulin +pouliot +poulos +poulsen +poulter +poustchi +powell +power +powers +powlick +pownall +powney +poyer +poyi +poyner +poynting +pozzi +ppaul +prab +prabaddh +prabhaka +prabhu +prabir +prachaya +pracht +prada +pradeep +pradip +pradnyan +prado +pradyumn +praeuner +prafula +pragna +prakash +pramod +prams +pranav +prang +prasad +prasada +prasanna +prashad +prashant +prashaw +pratap +pratapwa +pratibha +pratt +prattico +pravato +praveen +pravin +praxis +praysner +prayson +prchal +precoda +predel +predon +preece +prelims +prem +pren +prent +prentice +prentiss +preo +prescott +presgrov +presley +presner +presotto +pressbur +presson +presti +prestia +prestipi +preston +preston- +prestrud +presutti +preuss +prevatt +preville +previn +prevost +prewitt +pria +pribhu +price +prichard +pricing +prickett +pridgen +priede +priestle +prikkel +primeau +prince +pringle +print +printers +printing +prints +printsup +prinz +priore +pris +prisca +priscell +priscill +prissie +pritchar +prithvi +priti +prity +privett +priviter +priya +probert +problems +probs +procacci +procca +procner +procter +prodmfg +prodmgmt +producti +prof +proffit +prog +program +program- +proj +projects +projofc +prokes +prokop +prokopen +promac +propes +prosise +prosperi +pross +prosyk +prototyp +proudfoo +proulx +provenca +provench +pru +prudence +prudi +prudy +prue +pruett +pruitt +prunier +prupis +prybyla +pryce +prymack +pryor +prystie +pryszlak +przewloc +przybyci +psce +pseudony +psklib +psutka +ptefs +ptolemy +publicat +pubs +puchala +puckett +puddingt +pue-gilc +puelma +puent +puett +puetz +puff +pufpaff +pugas +pugh +puglia +pui-wah +pujara +pulak +pulcher +pulcine +pulitzer +pullan +pullum +pulver +punch +pundyk +puneet +pung +punsalan +puran +purcell +purchasi +purdy +purgerso +puringto +purnam +purnell +purnima +purohit +purposes +purshott +purson +puryear +pushelbe +pusun +pusztai +putman +putnam +putnem +puukila +pye +pyle +pyles +pyng +pyong +pyotr +pyron +python +qadir +qadri +qainfo +qainsp +qi-de +qide +qihan +qiming +qin +qing +qing-hui +qinghui +qingyan +qiuyun +qizhong +qu +quabidur +quality +quan +quane +quang +quang-tr +quante +quantril +quarles +quarterm +quattruc +quayle +queenie +quek +quelch +quennevi +quensett +quent +quentin +querenge +querida +queries +quesnel +questell +quevillo +quigley +quijano +quill +quillan +quilty +quincey +quincy +quinhon +quinlan +quinn +quinones +quint +quinta +quintana +quintero +quintill +quintin +quintina +quinton +quintus +quinz +quite a +quixote +quizmast +quoc +quoc-vu +quocanh +quoi +quon +qureshi +quyen +quynh +raab +raaf +raaflaub +raanan +rab +rabadi +rabaglia +rabatich +rabbi +rabecs +rabenste +rabi +rabiasz +rabie +rabin +rabipour +rabjohn +rabon +rabzel +racette +rachael +rached +rachel +rachele +rachelle +rachmani +racicot +racine +racioppi +racz +rad +radames +radcliff +raddalgo +raddie +raddy +radek +radford +radha +radick +radio +radko +radojici +radomir +radoslav +radovnik +radulovi +radvanyi +rae +raeann +raejean +raf +rafa +rafael +rafaela +rafaelia +rafaelit +rafaelll +rafaello +rafe +rafek +raff +raffaell +raffarty +rafferty +raffi +rafflin +rafi +rafik +rafiq +rafol +rafols +rafter +raftery +ragan +ragbir +ragde +raghav +raghava +raghavan +ragheb +raghu +raghunat +raghuvir +ragland +raglin +ragnar +ragsdale +ragu +ragui +raha +rahal +rahardja +rahdar +rahel +rahimtoo +rahm +rahman +rahmani +rahmany +rahmatal +rahn +rahrer +rahul +raicu +raif +raigwell +raila +railey +raimondo +raimund +raimundo +raina +raine +rainer +raines +rainey +raing +rainmake +rains +rainsfor +raissian +raiswell +raj +raja +rajadasa +rajagopa +rajala +rajan +rajani +rajanika +rajapaks +rajarshi +rajat +rajcher +rajchgod +rajchwal +rajczi +rajeev +rajen +rajendra +rajesh +rajeswar +rajguru +rajinder +rajini +rajiv +raju +rajwani +rakeim +rakel +raker +rakesh +rakhal +rakhuma +rakochy +rakotoma +raleigh +raley +ralf +rali +ralina +ralph +ralston +ram +rama +ramachan +ramadoss +ramage +ramah +ramakant +ramakesa +ramakr +ramakris +ramamoor +raman +ramana +ramanamu +ramanan +ramanand +ramanath +ramani +ramaprak +ramarao +ramaswam +rambo +rambow +ramee +ramesh +ramey +ramez +rami +ramin +ramirez +ramirez- +ramiro +ramiz +ramkisso +ramkumar +ramlogan +ramnarin +ramneek +ramnikla +ramon +ramona +ramonda +ramondt +ramos +rampaul +rampino +ramroop +ramsaran +ramsay +ramsayer +ramsden +ramses +ramsey +ramseyer +ramzi +ramzy +ran +ran-joo +rana +ranahan +ranald +ranbir +rance +rancell +rand +randa +randal +randall +randecke +randee +randel +randell +randene +randhawa +randhir +randi +randie +randolf +randolph +randy +ranea +ranee +ranette +raney +ranga +rangan +ranganad +ranganat +rangasam +rangaswa +rangchen +rangel +ranger +rangooni +rani +rania +ranice +ranieri +ranique +ranjan +ranjit +rank +rankin +ranna +rannells +ranney +ranoa +ranoska +ransell +ransom +ranson +rantala +ranvir +rao +raouf +raoul +raphael +raphaela +rappopor +raquel +raquela +rasberry +raschig +rashed +rashedi +rashid +rashid-a +rashidah +rashidi +rashmi +rasia +rasla +rasmus +rasmusse +rasselas +rassell +rastelli +rastogi +ratcliff +rathbun +rathnaku +ratko +ratnam +ratnayak +rattanap +rattray +ratz +rau +raud +raudres +rauen +raul +rausa +rausch +raven +raves +ravi +ravid +ravinder +ravindra +raviv +ravji +rawley +rawnoi +raxter +ray +rayan +raychel +raye +rayl +rayleigh +rayment +raymona +raymond +raymund +rayna +raynald +raynard +raynell +rayner +raynor +rayshell +raz +razavi +rccl +rch +rchisn +rchlab +rea +read +reade +reader +readling +readme 3 +reagan +reagen +real +realtime +reamonn +rean +reates +reaume +reaves +reavis +reba +rebbecca +rebe +rebeca +rebecca +rebecka +rebeka +rebekah +rebekkah +rebel +rec +recabarr +receivin +rechelle +reckhard +recktenw +records +recsnik +recyclin +red +reda +redbeard +redd +reddick +reddigan +redding +reddingt +reddy +redfoot +redford +redgie +redinbo +redish +redman +redmond +redshaw +redway +ree +reeba +reece +reed +reeder +reena +rees +reese +reeta +reetz +reeva +reeve +reeves +refat +refuerzo +reg +rega +regan +rege +regen +reggi +reggie +reggis +reggy +regier +regimbal +regina +reginald +reginaul +regine +reginia +regis +register +regnier +rego +rehbein +rehder +rehel +reich +reichenb +reiching +reichman +reichow +reid +reidar +reidelbe +reider +reif +reifschn +reijerke +reiko +reilly +reiman +reimann +reimburs +rein +reina +reinald +reinaldo +reinboth +reind +reine +reiner +reinhard +reinhold +reinink +reinke +reinlie +reinman +reinold +reinwald +reis +reiser +reiss +reist +reiter +reitfort +reith +reitling +rejean +rejeanne +reka +rekowski +relation +reller +rem +rembecki +rembish +remedios +remers +remi +remillar +remingto +remitha +remo +remon +remrey +remson +remus +remy +ren +rena +renado +renae +renaldo +renard +renata +renate +renato +renaud +renault +rendell +rendon +rene +rene-ala +reneau +renee +renell +renelle +renema +renette +renfro +renfroe +renganat +renie +renita +renken +renmarie +renner +rennie +rennolds +renny +reno +renoir +renold +renton +renu +renwick +repair +repeta +reportin +reports +requel +requests +research +resende +resnick +ress +ressner +rester +restore +restrepo +results +resve +reta +retallac +retallic +retha +rettie +reube +reuben +reubens +reuss +reuven +reva +revah +revelle +revill +revis +revkah +rewitzer +rex +rexford +rexroad +rey +reyad +reyaud +reydman +reyes +reyna +reynaldo +reynard +reynold +reynolds +reza +rezaian +rezansof +rezneche +reznick +reznik +rezzik +rfa +rfeynman +rff +rhattiga +rhea +rheal +rheault +rheaume +rheba +rheta +rhett +rhetta +rhew +rhiamon +rhianna +rhianon +rhine +rhoades +rhoads +rhoda +rhodeniz +rhodes +rhodia +rhodie +rhodri +rhody +rhona +rhonda +rhough +rhyndres +rhys +rhyu +ri +ria +riad +rialland +riane +riannon +rianon +riaz +ribakovs +ribaldo +ribi +ribordy +ribot +ric +rica +ricard +ricardo +ricca +riccardo +riccitel +ricciuto +rice +rich +richad +richard +richardo +richards +richart +richelle +richer +richey +richie +richlark +richman +richmond +richmoun +richter +richy +rici +rick +rick-jan +rickard +rickborn +rickel +ricker +rickert +ricketso +ricketts +rickey +ricki +rickie +rickjan +rickrd +ricks +ricky +rico +ricoriki +riddall +ridder +riddick +rider +ridge +ridgeway +ridgewel +ridgway +ridha +ridley +riebl +ried +riedel +riehle +riekie +rieko +rieni +rigby +rigdon +rigel +riggins +riggs +riggsbee +righter +rightmir +rigobert +rigsbee +rigstad +rijn +rijos +rijswijk +rik +riki +rikki +rikley +riley +rilla +rima +rimantas +rimey +riml +rimmler +rimsa +rina +rinaldo +rinawi +ring +ringo +rini +rintala +rintel +rintoul +rio +riobard +riopel +riopelle +riordan +rios +riou +rioux +rip +ripa +ripley +risa +risdal +risher +rishy-ma +risko +risler +rist +risto +rita +ritalynn +ritchey +ritchie +ritenour +rittenho +ritter +rittmann +ritz +ritza +ritzmann +riva +rivaherr +rivalee +rivard +rivera +rivers +rivest +rivi +rivkah +rivy +rix +riyad +riyaz +rizal +rizewisk +rizk +rizky +rizwan +rizzardi +rizzo +rizzuti +rk +rnashcro +ro +roana +roanna +roanne +roarke +rob +robann +robart +robb +robbert +robbi +robbie +robbin +robbins +robby +robbyn +robeling +robena +robenia +roberge +robers +roberson +robert +roberta +roberto +roberts +robertso +robieux +robillar +robin +robina +robinet +robinett +robinia +robins +robinson +robinwil +robitail +robles +robling +robney +robson +robustne +roby +robyn +rocco +roch +roche +rochell +rochella +rochelle +rocheste +rochette +rochon +rocio +rock +rocke +rockey +rockford +rockie +rockley +rockly +rockwell +rocky +rod +roda +rodd +roddick +roddie +roddy +rodely +roden +rodenfel +rodenhui +rodent +roderic +roderich +roderick +roderigo +rodge +rodger +rodgers +rodgin +rodi +rodie +rodina +rodkey +rodney +rodolfo +rodolph +rodolphe +rodrick +rodrigo +rodrigue +rodrigus +rodrique +rodschat +roe +roebling +roedel +roehl +roehrig +roel +roelof +roelofs +roemer +roerick +roesler +roeten +rog +rogan +rogelio +roger +rogerio +rogers +roget.wo +rogge +rogne +rogness +rognlie +rogoff +rogue +rohal +rohan +rohe +rohit +rohtert +roi +roieh +roig +rois +roithmai +roj +rojas +rojer +rok +rokas +roland +rolande +rolando +roldan +roleson +roley +rolf +rolfe +rolfes +rolland +rollie +rollin +rollins +rollinso +rollo +rolls +rolly +rolnick +rolph +rolston +roly +rolyn +roma +romagnin +romain +roman +romanchu +romani +romano +romanows +rombeek +romberg +rombough +romeo +romero +romi +romina +rommel +rommell +romola +romolo +romona +romonda +romulus +romy +ron +rona +ronaald +ronak +ronald +ronalda +ronaldo +ronalds +ronaldso +ronan +ronda +rondeau +ronen +ronendra +roney +rong +rong-che +rong-chi +rong-jen +rong-jwy +rongchei +ronghui +rongjen +rongjwyn +roni-jea +ronica +ronitt +ronkus +ronn +ronna +ronneke +ronni +ronnica +ronnie +ronny +roob +roobbie +roohy-la +rooney +roosevel +root +roots +roozbeh +roper +roque +rora +rori +rorie +rorke +rory +ros +rosa +rosabel +rosabell +rosado +rosaleen +rosalia +rosalie +rosalind +rosaline +rosalyn +rosalynd +rosamond +rosamund +rosana +rosanna +rosanne +rosario +rosch +rosche +rosco +roscoe +rose +roseann +roseanna +roseanne +rosebud +roseland +roselia +roselin +roseline +rosella +roselle +rosemari +rosemary +rosemond +rosen +rosenbau +rosenber +rosenblu +rosendal +rosene +rosenfel +rosenqui +rosentha +roser +rosetta +rosette +rosewell +rosey +roshelle +rosie +rosien +rosina +rosita +roski +rosko +roslyn +rosmunda +rospars +ross +ross-ada +ross-ros +rossanes +rosser +rossi +rossie +rossigno +rossingt +rosson +rossy +rosvick +rosy +roszko +rotenber +roth +rothamel +rothey +rothwell +rotondo +rotzjean +rouer +rouhad +rouleau +roulez +roundy +roupen +rourk +rourke +rous +rousseau +rousset +roussier +roussin +roussy +routhier +routing +rouvin +row +rowan +rowatt +rowe +rowell +rowen +rowena +rowhani +rowland +rowlands +rowley +rowney +rowsell +roxana +roxane +roxanna +roxanne +roxi +roxie +roxine +roxy +roy +roya +royal +royall +royals +royce +roychowd +royden +royer +royle +royster +roz +rozaini +rozalia +rozalie +rozalin +rozamond +rozanna +rozanne +roze +rozele +rozella +rozelle +rozen +rozett +rozier +rozin +rozina +rozon +rozumna +rriocard +rtingres +rtp +rtpbuild +rtprel +rtprelb +ru +ruane +ruaud +ruban +rubanovi +rube +ruben +rubens +rubenste +rubetta +rubi +rubia +rubie +rubin +rubina +rubinfel +rubinov +rubinste +rubio +ruby +ruchel +ruchi +ruck +ruckman +rud +rudd +ruddell +ruddick +ruddie +ruddle +ruddy +rudell +rudi +rudiak +rudich +rudie +rudiger +rudin +rudis +rudisill +rudolf +rudolfo +rudolph +rudy +rudyard +rudzinsk +rudzitis +rueben +ruecha +ruediger +ruel +ruest +ruetz +ruey +rufe +ruffolo +rufino +rufus +rugg +ruggiero +rui +rui-tao +rui-yuan +ruigrok +ruitao +ruiz +rumley +rummans +rummel +rummell +runciman +rundle +rundstei +rungroj +runkel +runnels +running +runyon +ruoh-chy +ruohchyu +rupa +rupert +ruperta +ruperto +rupnow +rupp +ruppert +ruprecht +ruqiang +rurick +rurik +rusch +ruschmei +rushing +rushmore +rushton +rusin +ruspini +russ +russel +russell +russett +rustie +rustin +rustu +rusty +rutger +ruth +ruthann +ruthanne +ruthart +ruthe +rutherfo +ruthi +ruthie +ruthy +rutland +rutledge +rutt +ruttan +rutter +ruttger +rutulis +rutyna +ruud +ruy +ruyant +ruyle +ruzicka +ruzycki +ryall +ryals +ryan +ryann +rybczyns +rycca +ryce +rychlick +ryde +ryder +rydhan +ryerson +rygwalsk +rykwalde +ryley +rylott +ryman +rymkiewi +rynders +rynties +ryohei +ryon +ryoung +ryszard +ryun +rzepczyn +sa'id +saad +saake +saal +saatciog +saavedra +saba +sabadash +sabah +sabanaya +sabat +sabatell +sabatini +sabatino +sabbagh +saber +saberi +sabety +sabiha +sabin +sabina +sabine +sabo +sabol +sabooria +sabourin +sabra +sabri +sabrina +sabry +sabuson +sabzali +sacarell +sacchett +sacha +sachidul +sachiko +sachindr +sachs +sacks +sacto +sada +sadan +sadath +sadegh +sadeghi +sadella +sadie +sadler +sadorra +sadowska +sadoyama +sadri +sadroudi +sadru +sadye +saed +saeed +saeid +safaa +safah +safinia +sagan +sage +sager +sagers +sagris +saha +sahay +sahib +sahinalp +sahli +saibal +saibun +said +saidee +saidzade +saied +saifalla +saifulla +saify +saiid +saikaley +sails +saini +sainsbur +saisho +sait +saito +saitoh +saiyed +sakaguch +sakai +sakamaki +sakauye +sakus +sal +salada +saladna +salah +salaidh +salam +salamon +salapek +salazar +salb +salcudea +saldanha +saleem +saleh +salehi +salem +salembie +salemi +sales +salgado +salhany +salibi +salim +salim-ya +salimi +salina +salinas +salis +salkilld +salkini +salkok +salladay +salle +sallee +sallehud +salli +sallie +sally +sallyann +salmon +saloma +salome +salomi +salomo +salomon +salomone +salsbery +saltamar +salter +saltside +salva +salvador +salvato +salvator +salvidor +salvin +salwa +salyer +salyniuk +salzillo +sam +samac +samalot +saman +samantha +samara +samaratu +samaria +samaroo +sambar +sambi +sambo +sameh +samhaber +sami +samia +samieian +samir +sammie +sammon +sammons +sammy +samora +sampalea +sampat +sampath +samples +sampson +sampson- +samshixu +samson +samsonen +samual +samuel +samuele +samy +sanaa +sanabria +sanae +sanand +sanborn +sanche +sanchez +sancho +sanda +sandberg +sandburg +sande +sandeep +sandell +sander +sanders +sanderso +sandford +sandhar +sandhu +sandhya +sandi +sandie +sandifor +sandip +sandison +sandiway +sandler +sandlfor +sandner +sandness +sandor +sandra +sandre +sandrine +sandro +sandrock +sandy +sandye +sanford +sanford- +sang +sang-mau +sang-woo +sangbong +sangha +sanghami +sangho +sangiova +sangman +sangwook +sanh +sanity +sanja +sanjay +sanjeet +sanjeev +sanjib +sanjiv +sanjiva +sanjoy +sankey +sanks +sanoy +sanramon +sanschag +sansom +sanson +sansone +santa +santabar +santella +santi +santiago +santiest +santitor +santo +santos +santosh +sanzone +sapena +saphir +sapphira +sapphire +saqib +sara +sara-ann +saraann +sarah +sarajane +saran +saran-br +sarangar +sarasina +sarath +saravano +sarawath +sarbutt +saree +sarena +sarene +sarette +sarge +sargent +sargeson +sari +sarin +sarina +sarine +sarioglu +sarita +sarkari +sarlos +sarma +sarna +saroj +sarracin +sarrasin +sarrazin +sarsh +sarson +sartin +sartiran +sarto +sartor +sarubbi +sasaki +sascha +sasha +sashenka +sashi +sasinows +sask +saskia +sasore +sassan +sassine +sasson +sastry +saswata +sathe +sati +satin +satis +satish +satkamp +satkunas +sato +satoh +satoshi +satta +sattar +satterfi +sattler +satya +satyajit +satyanar +saucerma +sauck +sauder +saudra +sauer +saul +saulnier +sauls +saumitra +saumure +saumya +sauncho +saunder +saunderc +saunders +saundra +saungika +sauprobo +sauvagea +sauve +sauveur +savadkou +savanh +savard +savarimu +savaryeg +savina +savino +savita +savo +savoie +savoj +savoula +saw +sawada +saward +sawaya +sawchuk +sawczyn +sawita +sawsan +sawyer +sawyere +sawyers +sax +saxe +saxena +saxon +say +sayar +sayed +sayeeda +sayegh +sayer +sayers +sayla +sayre +sayres +scalabri +scalera +scales +scammerh +scamurra +scandret +scanga +scanlan +scanlon +scapin +scarboro +scarbrou +scarface +scarffe +scarlet +scarlett +scarrow +scatena +scates +schaap +schacham +schachtl +schack +schadan +schaefer +schafer +schaffel +schaffer +schallen +schaller +schallio +schanck +schank +schanne +scharf +schartma +schatzbe +schauer +schavo +schavone +schechtm +scheck +scheckle +schecter +schedule +scheduli +scheer +scheffle +scheible +scheidt +scheifel +schejbal +schell +schellen +schembri +schemena +schenck +schendel +schenk +schenkel +schepps +scherbin +scherer +schermer +scherzin +schesvol +scheuerm +schick +schieber +schiefer +schiegl +schierba +schill +schiller +schillin +schiltz +schinkel +schipper +schireso +schirmer +schirtzi +schissel +schittl +schlacht +schlagen +schlange +schledwi +schlegel +schlemme +schlicht +schloboh +schluter +schmadtk +schmeder +schmeing +schmeler +schmelze +schmidt +schmitig +schmitt +schmitz +schmoe +schnacke +schnaith +schneide +schnell +schnirer +schnob +schnupp +schnurma +schober +schoch +schoen +schoener +schoenfe +schoenin +schoenli +schoettl +schofiel +scholes +scholey +scholman +scholtz +schonber +schooley +schopenh +schousbo +schouwen +schrader +schrag +schrage +schram +schraner +schrang +schreibe +schreier +schreife +schreine +schrier +schroede +schroer +schroff +schruefe +schrupp +schrybur +schubert +schuck +schucker +schuddeb +schuett +schuette +schuld +schulte +schultz +schultze +schulz +schulze +schumach +schumann +schuster +schute +schutte +schutz +schuyler +schvan +schwab +schwader +schwalba +schwane +schwante +schwartz +schwarz +schwenk +schyndel +schyving +scibek +scif +scissons +scodras +scomello +sconzo +scooter +scorpio +scorziel +scot +scott +scotti +scottie +scottjop +scottt +scotty +scournea +scovell +scovill +scp +scpbuild +scpiivo +scptest +scrantom +scrbacic +screener +scribner +scrivens +scroger +scss +scssdev +scully +scurlock +scythia +se +seabrook +seager +seagle +seagrave +seagrove +seahawk +seale +sealy +seamster +seamus +sean +seana +seang +seanna +seany +searl +searle +searles +sears +seatter +seawell +seay +sebastia +sebastie +sebata +sebeh +sechang +sechen +sechrest +secrest +security +seda +sedat +sedayao +seddigh +seddon +sedigheh +sedovic +sedran +see +seeds +seegobin +seelaend +seelan +seeler +seeley +seema +seenu +seery +sees +seethara +segal +segars +seggie +seguin +sehat +sehgal +sehinson +sehmbey +sehyo +sei +seidel +seiden +seidl +seidman +seifers +seifert +seifried +seiji +seiko +seiler +seiple +seipue +seitz +seiz +sej +sek-ming +seka +sekar +sekhar +seki +sekiguch +sekming +sekuler +sela +selbrede +selby +selchow +selcuk +seldon +selena +selene +selent +selestin +selia +selic +selie +selig +selim +selime +selina +selinda +seline +seliske +selisker +selkirk +sella +sellars +selle +sellers +sellgren +sellis +sells +sellwood +selma +selva +selvaraj +selwyn +sembi +semeniuk +semenzat +semerau +semler +semmens +semmler +semoon +sena +senad +senderow +sendyk +senecal +senese +sengoba +sengupta +seniuk +senng +senser +senten +sentner +sentovic +senyildi +senyshyn +seob +seoju +seok +seong +seoul +sepe +sepesi +sephira +sepko +serack +serafin +seraphin +serapin +serazzi +serban +serber +serbin +serbus +serdar +serduke +seregely +serena +serene +serethia +serge +sergeant +sergei +sergent +sergey +sergi +sergio +sergiu +sergo +seroka +serour +serraf +serrano +serre +servais +servance +services +servidio +serville +seshadri +seshan +seth +sethi +sethian +setiawan +seto +setsuko +settels +setterfi +settles +seufert +seumas +seung +seungbin +seungchu +seungjun +seuss +seven +severin +severina +severn +severns +sevigny +sevilla +seville +seward +sewell +sey-ping +seyar +seyed +seyfolla +seyma +seymour +sezer +sfiroudi +sforza +sgorniko +sguigna +sha-wen +shabatur +shabbir +shabo +shacham +shackelf +shacklef +shacklet +shackley +shaddock +shadow +shae +shafer +shaffer +shafik +shafiq +shafique +shahab +shahan +shahani +shahen +shahid +shahram +shahriar +shahrin +shahrokh +shahrood +shai +shaib +shaibal +shaida +shaila +shailan +shailen +shailend +shailesh +shailin +shaina +shaine +shaji +shaker +shakeri +shakib +shakil +shakoor +shakor +shalizi +shalla +shalmon +shalna +shalne +shalom +shama +shamblin +shames +shamim +shamir +shamji +shams +shamshad +shamshir +shamsia +shamus +shan +shan-min +shan-pin +shana +shanahan +shanan +shanda +shandee +shandeig +shandie +shandra +shandy +shane +shaner +shang +shang-ti +shangi +shangtia +shani +shanie +shankar +shanlin +shanmin +shanna +shannah +shannan +shannen +shannon +shanon +shanping +shanta +shantee +shanti +shantz +shao +shao-she +shaoshen +shapcott +shapin +shapiro +shapland +shappir +shara +sharad +sharada +sharae +sharai +sharan +sharee +shari +sharia +sharif +shariff +sharissa +sharity +sharkey +sharky +sharl +sharla +sharleen +sharlene +sharline +sharma +sharman +sharmila +sharnoff +sharon +sharona +sharone +sharpe +sharratt +sharri +sharron +sharry +shary +sharyl +sharyn +shashank +shashi +shastri +shastry +shatter +shattuck +shau +shaughan +shaughn +shaughne +shaukat +shaumil +shaun +shauna +shaupoh +shaver +shaw +shaw-yun +shawen +shawn +shawna +shawnee +shawyune +shay +shay-pin +shayanpo +shayla +shaylah +shaylyn +shaylynn +shayna +shayne +shayping +shea +sheaffer +shealy +shean +sheara +shearer +shearin +shearman +shears +sheba +shebanow +shechtma +shedd +shedman +sheehan +sheela +sheelagh +sheelah +sheena +sheeree +sheergar +sheets +sheff +sheffey +sheffie +sheffiel +sheffy +sheidafa +sheikh +sheikna +sheila +sheila-k +sheilah +sheilaka +sheileag +shein +shek +shekar +shekhar +shekwan +shel +shela +shelagh +shelba +shelbi +shelby +shelden +sheldon +shelegey +shelia +shell +shelley +shelli +shellie +shellin +shellman +shelly +shellysh +shelton +shem +shemwell +shen +shen-zhi +shena +sheng +sheng-fu +shengfu +shengru +shengwen +shengwu +shennan +shep +shepard +shepherd +sheppard +shepperd +sher +sherali +sherard +sheraton +sherban +shere +sheree +sheremet +sheri +sheri-ly +sheridan +sherie +sherif +sherill +sherilyn +sherin +sherk +sherline +sherlock +sherm +sherman +shermie +shermy +sherona +sherow +sherra +sherrard +sherrel +sherrell +sherrer +sherri +sherrie +sherrill +sherrily +sherry +sherrye +sherryl +sherwan +sherwin +sherwood +sherwyn +sherwynd +sherye +sheryl +sheth +sheu +sheung +shew +shewchen +shi +shi-qin +shi-wei +shiang-y +shiangyi +shiao-mi +shiaomin +shibahar +shibata +shibberu +shibo +shieff +shieh +shiel +shiela +shields +shiell +shier +shiffer +shiflett +shigeaki +shigeki +shigemur +shigenao +shigeo +shigeru +shih +shih-dar +shih-hai +shih-hsi +shih-kua +shih-tie +shihhai +shihhsiu +shihkuan +shihtien +shik +shikui +shila +shiley +shilla +shilling +shimada +shimandl +shimiz +shimizu +shimshon +shin-dug +shina +shinder +shindug +shing +shing-ch +shing-mi +shingche +shingler +shingmin +shinichi +shinji +shinjo +shinobu +shinohar +shinzo +shiou +shipe +shipp +shippen +shiqin +shiquan +shir +shirai +shiranth +shiratsu +shiraz +shireen +shireman +shirene +shirey +shirin +shirinlo +shirish +shirl +shirlee +shirleen +shirlene +shirley +shirley- +shirline +shirman +shiroshi +shirriff +shishakl +shishido +shiu +shiu-lin +shiuan +shiue +shiuling +shiun +shiung +shiv +shiva +shivaji +shivapra +shivchar +shivdars +shivnan +shiwei +shixian +shlomo +shmoys +shnay +shnider +sho +shoaf +shobana +shockley +shoeb +shoemake +shoens +shoji +sholom +shon +shona +shonda +shonka +shonuck +shoou-yu +shoouyu +shop +shoppel +shorgan +shorwan +shoshana +shoshann +shostak +shou +shou-che +shou-mei +shoucher +shoulars +shouli +shoun +shouresh +showers +shreve +shriberg +shrieves +shripad +shriram +shtivelm +shtulman +shu +shu-chen +shu-gong +shu-mei +shuang +shuangli +shubaly +shuchen +shue +shuechia +shuen +shugong +shuichi +shuji +shukor +shukster +shuler +shull +shultz +shum +shuman +shumate +shumei +shunfeng +shung +shunhui +shunmuga +shunro +shuo +shupe +shuqing +shurlock +shurtlef +shurwood +shuster +shusuke +shute +shutler +shutoku +shutterb +shuvra +shuyen +shwed +shwu-chy +shwuchyn +shya-yun +shyam +shyan +shyh-chi +shyhchin +shylo +shyoko +shypski +shyu +si +siamack +siamak +siana +sianna +siaw +sib +sibbet +sibbie +sibby +sibeal +sibel +sibella +sibelle +sibiga +sibilla +sibincic +sibley +sibyl +sibylla +sibylle +sicard +sich +sichao +sickle +sickler +sicotte +sid +siddall +siddell +siddiqui +sides +sidhu +sidnee +sidney +sidone +sidoney +sidonia +sidonnie +sidor +sidorovs +sieben +sieber +siefert +siegal +siegel +siegfrie +siegle +siegmund +siegurd +siehl +sieling +siemens +siemer +sienggo +siew +siew-kia +siewert +sifer +siffre +sig +sigda +sigfrid +sigfried +siggy +sigismon +sigismun +sigmon +sigmund +signe +sigrid +sigurd +sigurdso +sigut +sigvard +siham +sihem +sik-yin +sika +sikander +sikes +sikita +sikri +sil +silang +silas +silburt +sile +sileas +silgardo +silianu +silieff +silins +sills +sils +silva +silvain +silvan +silvana +silvano +silvanus +silverma +silverst +silverth +silveste +silvestr +silvia +silvie +silvio +silwer +sim +simaan +simanski +simard +simard-n +simcha +simcoe +simcox +sime +simen +simeon +simeone +simhan +simion +simkin +simler +simmonds +simmons +simms +simon +simon-ch +simon-pu +simona +simone +simonett +simonian +simonne +simonovi +simons +simonsen +simpkin +simpson +simren +sims +simser +simson +simulati +simzer +sina +sinanan +sinasac +sinchak +sinchau +sinclair +sinclare +sindee +sing +sing-pin +singbeil +singer +singh +singhal +singham +singires +singyu +sinh +sinha +sinkfiel +sinkovit +sinnett +sinnott +sinoyann +sinyor +siobhan +siomalas +siotong +sioux +siouxie +siperco +sipes +siping +sipple +sir +sirevici +siripong +sirojith +sisely +sisile +sisk +siso +sissela +sissie +sissy +sist +sitar +sitch +sitler +siu +siu-kwok +siu-ling +siu-man +siusan +siv +siva +sivaji +sivasoth +siward +sizto +sj +sjaak +sjerps +sjouke +skaff +skaftaso +skaggs +skalski +skanes +skaret +skariex +skedelsk +skeeter +skef +skell +skelly +skene +skeoch +skerlak +skerry +skeuse +skiba +skiclub +skillen +skillman +skinner +skip +skipp +skipper +skippie +skippy +skipton +sklower +skoberne +skof +skopliak +skrebels +skriverv +skrobans +skrobeck +skruber +skuce +skuratow +skwarok +sky +skye +skylar +skyler +slaa +slabaugh +slaby +slade +sladek +slagel +slartiba +slatteng +slattery +slautter +slavin +sldisk +sleeman +sleeth +slempers +slick +slinkard +slinowsk +sliter +sloan +sloane +slobin +sloboda +slobodia +slobodri +slonosky +slotnick +slozil +sluis +slunder +slusser +sly +slyteris +smale +smalltal +smallwoo +smecca +smedema +smeenk +smelters +smerdell +smerek +smid +smine +smit +smita +smith +smithdea +smithson +smits +smitty +smolin +smook +smoot +smothers +smrke +smrke-su +smuda +smulders +smyrl +smyth +smythe +snair +snapper +snarr +snead +snedden +snedeker +snehal +snelgrov +snelling +snider +sniderma +snipes +snips +snodgras +snoke +snorri +snowden +snuggs +snyder +soard +sobchuk +sobczak +sobeck +sobel +sobiesia +sobitha +sobkow +sobolak +sobolews +sobon +sochovka +socorrit +socrates +sodano +soderber +sodhi +soebowo +soegiono +sofeya +soffa +sofia +sofie +sofoklis +soh +sohaib +sohail +sohal +sohale +sohayla +sohier +sohni +sohns +sohota +soiffer +soin +sojka +sojkowsk +sokolows +sokyrko +sol +solai +solange +soldera +solheim +soliman +solita +solkoff +sollee +sollie +sollman +solly +solman +solodko +solomon +solovay +solski +soman +somani +somenzi +somera +somers +somerset +somervil +somisett +sommer +sommerdo +sommerfe +sompong +somppi +somsak +son +sonbol +sondra +sondueim +song-cha +song-ho +songchar +songho +songnian +sonhing +sonia +sonier +sonja +sonne +sonni +sonnie +sonnnie +sonny +sono +sonoda +sonoe +sonya +soo +sood +soohong +soohoo +sook +sookdeo +sooley +soong +soonhoi +sophey +sophi +sophia +sophie +sophroni +sorathia +sorbi +sorcha +soreanu +soren +sorensen +soriano +sorin +sorkin +soroker +sorrel +sorrell +sorrenti +sos +sosa +sosanna +sossaman +sotelo +sotiriad +sotiris +soto +sotos +souba +soucie +soucy +sougata +souheil +soulef +soules +soullier +soumis +soumitra +sounya +souphala +sources +souren +sourin +sourisse +sourour +sousa +soussa +southard +souther +southon +southwor +souza +sova +soweidan +sowry +soyeh +soyland +soyong +soyster +soyuer +space +spallin +spann +spannbau +sparacio +sparkes +sparks +sparksma +sparky +spass +spaugh +speakec +speaker +spearman +spearpoi +spears +specs +speedy +speer +speers +speight +spejewsk +spence +spencer +spense +spenser +sperman +spessot +spicer +spickelm +spieker +spieler +spight +spike +spilchak +spillane +spily +spindler +spinelli +spingola +spinks +spirakis +spirkovs +spiros +spisak +spitzer +spivey +splitt +spohn +spolar +sponagle +sponchia +spooner +spragg +spraggin +sprague +sprandel +sprayber +spriggs +spriging +springth +sprott +sproul +sproule +sprouse +spruell +sprules +sprunger +spudboy +spurlin +spurlock +spurway +spy +spyridon +spyros +squires +squizzat +sr +srawan +src +sreedhar +sri +sridaran +sridevi +sridhar +sridhara +srihari +srikanth +srikrish +srimurti +srinath +srini +sriniuas +srinivas +sriram +sriranja +srirupa +sriv +srivasta +srivatsa +sroczyns +ssi +ssington +st +st-amour +st-denis +st-louis +st-marti +st-onge +st-pierr +st. +st.clair +st.denis +st.germa +st.jacqu +st.john +st.laure +st.louis +st.pierr +st.vil +st_james +staats +stabilit +stace +stacee +stacey +stach +stachowi +staci +stacia +stacie +stackpol +stacy +stadelme +stadler +stafani +staffard +staffeld +staffing +stafford +staford +stagger +staggs +stagmier +stahl +stahly +stainbac +staley +stalin +stallabr +stallcup +stalling +stalter +stamboul +stampfl +stampley +stamps +stan +stanciu +stanczyk +standard +standel +standen +standfor +standrin +stanfiel +stanford +stange +stanisla +stanke +stanleig +stanley +stanly +stansber +stansbur +stansby +stansell +stansfie +stanton +stanulis +stanwood +stapenho +staples +star +starbuck +staring +starkaus +starkeba +starkes +starks +starla +starlene +starlet +starlin +starnes +starowic +starr +stars +starsdps +starzman +stasaski +stasiak +stasney +stastny +stasyszy +stat +staten +stateson +statile +statisti +staton +stavro +stavros +stayton +stclair +stctest +stds +ste +ste-mari +stearn +stearne +stearns +stebbing +steckley +steede +steele +steelman +steen +steenbur +steene +steeves +stefa +stefan +stefana +stefanac +stefania +stefanie +stefano +steffane +steffen +steffens +steffes +steffey +steffi +steffie +steffy +stegall +steggall +stegman +stegmuel +stehen +stehr +steidel +steiert +steinar +steinbac +steinber +steinhar +steip +stejskal +steklasa +stelcner +stella +stellita +stellwag +stemmler +stender +stennett +stenson +stensrud +stepchuk +steph +stepha +stephan +stephana +stephane +stephani +stephann +stephans +stephanu +stephany +stephe +stephen +stepheni +stephens +stephi +stephie +stephine +stepler +stepp +steranka +stercyk +sterczyk +sterescu +stergios +sterian +sterling +stern +sterne +stesha +steski +stetner +stetson +stetter +stettner +stevan +stevana +stevanov +steve +steven +stevena +stevens +stevenso +stevie +stevy +stew +steward +stewart +stewart- +sticklan +sticpewi +stiglitz +stiles +stillman +stillwel +stimler +stina +sting +stinky +stinson +stinzian +stirling +stirrett +stites +stjohn +stobaugh +stock +stocker +stocks +stockton +stockwel +stodart +stoddard +stoelzle +stoevsky +stoffels +stoker +stokes +stokker +stokoe +stokoski +stolzle +ston +stone +stonebra +stonehou +stoner +stonos +stooke +storace +storelli +storey +storm +stormi +stormie +stormy +storrie +story +stotts +stotz +stouder +stough +stovall +stover +stowe +stoyles +strachan +strackho +strader +straka +strandbe +strandlu +strannem +strasser +stratfor +stratton +straub +strauch +strauss +strautma +strawczy +strayhor +streater +streatfi +streibel +streight +streng +strickla +strider +strober +strock +stroemer +strohmey +strom +stronski +stropp +stroud +stroupe +strube +struble +strucche +strudwic +struzyns +stu +stuart +stubblef +stubbs +stuckey +stude +student +studer +stults +stumpf +sturdiva +sture +sturrock +stutts +su +su-xin +suany +suarez +suat +subasing +subhash +subhashi +subhi +subhra +subhrans +subi +subick +subissat +subitha +submital +subodh +subra +subraman +subroto +subsara +subu +sucha +suchitra +suchocki +suda +sudabeh +sudan +sudbey +sudbury +suddarth +sudeep +sudesh +sudha +sudhakar +sudhir +sudip +sue +sue-joe +sue-may +sueanne +suejoe +sueling +suellen +suen +suer +suess +sufcak +suffern +sugandi +sugarbro +sugarman +sugih +sugihara +suh +suha +suhail +suharly +suhas +suilin +suiping +suitt +sujay +suk-ho +suk-jae +suk-yin +sukey +sukhendu +sukho +sukhwant +suki +sukjae +sukku +sukumar +sula +sulatyck +sule +sulewski +suliguin +sullivan +sully +sultan +suman +sumanth +sumaryan +sume +sumi +sumit +sumitro +summach +summerli +summers +sumner +sumpter +sunatori +sundar +sundaram +sundares +sunderla +sundra +sung +sung-cho +sung-sup +sungchin +sungchon +sunghae +sungki +sungkyoo +sungsup +sunil +sunjay +sunnie +sunning +sunny +sunshine +sunstrum +suomela +supervis +support +suprick +supriya +supriyo +sura +surazski +surber +surendra +suresh +suria +surinder +surowani +surray +surreau +surridge +sursal +survey +surya +susan +susana +susanett +susann +susanna +susannah +susanne +susanto +susette +susi +susick +susie +susil +sussie +susumu +susy +sutardja +sutarja +sutarwal +sutcliff +suter +sutera +sutherla +suthers +sutija +sutphen +sutter +sutterfi +sutterli +sutton +suu +suvanee +suwala +suwanawo +suwandi +suxin +suyama +suykens +suyog +suzan +suzane +suzann +suzanna +suzanne +suzette +suzi +suzie +suzuki +suzy +svalesen +svante +sven +svend +svenn-er +svensson +sventek +svetlana +svilans +svm-bnrm +svo +svr +swact +swaden +swails +swaine +swamy +swandi +swann +swanson +swanston +swaranji +swartz +swazey +swd +swearing +swee-joo +sweeney +sweetnam +swen +swenberg +swensen +swenson +swepston +swiat +swiatkow +swick +swidersk +swinamer +swinburn +swinkels +swinks +swinney +swinson +swinwood +swisher +switchin +switzer +swope +swr +swyer +sy +syal +syamala +sybil +sybila +sybilla +sybille +sybyl +sycha +syd +sydel +sydelle +sydeman +sydney +sydnor +sydor +sydoryk +syed +sykes +syl +sylas +sylva +sylvain +sylvan +sylveste +sylvestr +sylvia +sylvie +sylvio +sym +syman +symen +symon +symons +syndra +synful +synness +syposz +syres +syrett +sys +sysadmin +sysint +syssuppo +systems +systest +syun +syyed +szabo +szamosi +szaplonc +szaran +szeto +sziladi +szkarlat +szopinsk +szot +szpakows +szpilfog +sztein +szuminsk +szura +szymansk +szypulsk +ta +ta-sung +tab +tabaja +tabalba +tabatha +tabb +tabbatha +tabbert +tabbi +tabbie +tabbitha +tabby +taber +tabina +tabitha +tabl +tabler +tables +tabor +tac +tachih +tacitus +tad +tadayuki +tadd +taddeo +taddeusz +taddio +tadeas +tadeo +tades +tadeusz +tadevich +tadge +tadio +tadlock +tae +tae-ho +tae-hwan +taeho +taehwan +taffy +taggart +taghizad +tague +tahamont +tahani +taharudd +taheri +tahir +tahsin +tai +tai-jen +tailinh +tailor +taina +tait +taite +taiwana +tajbakhs +tak +tak-wai +takagi +takahash +takahisa +takako +takao +takashi +takashim +takata +takayuki +takefman +takehiko +takeo +takeshim +takeuchi +taki +takis +takiyana +tal +talbert +talbot +talbott +talcott +talevi +talia +talis +tallett +tallia +tallie +tallou +tallulah +tally +talmont +talmy +talton +talya +talyah +tam +tamar +tamara +tamarah +tamarell +tamaresi +tamarra +tamas +tamasi +tamer +tamera +tami +tamiko +tamir +tamma +tammara +tammaro +tammi +tammie +tammy +tamqrah +tamra +tamrazia +tamura +tan +tan-atic +tana +tanaka +tanchak +tancordo +tandberg +tandi +tandie +tandiono +tandiwe +tandy +tanferna +tanglao +tangren +tanhya +tani +tania +tanio +tanir +tanitans +tanja +tann +tanner +tanney +tannie +tanniere +tanny +tansy +tanya +tao +tap +tapani +tape +tapner +tapp +tappende +tappert +tapsell +tara +tarah +tarak +taralp +taraneh +tarant +taranto +taraschu +tarasewi +tardif +tardiff +tardioli +tarek +taren +tareq +targosky +tarik +tariq +tarlamis +tarle +tarmi +tarnai +tarng +taro +tarof +tarquini +tarra +tarrah +tarrance +tarrant +tarsky +tarte +tarus +tarver +taryn +taryna +tas +taschere +tash +tasha +tasia +taskforc +taspatch +tassi +tasso +tassy +tasuk +tasung +tat +tata +tatangsu +tatar +tate +tatemich +tates +tateyama +tatiana +tatiania +tats +tatsdocn +tatsman +tatsugaw +tatsuya +tattenba +tatum +tatyana +tauberg +taul +tauna +taurus +tauscher +tauvia +tavana +tavares +taverner +tavis +tawauna +tawfik +tawnya +tawsha +taxashi +tay +tayeb +tayfun +taylor +taylor-h +tayyab +tc +tchangid +tchir +td +tdr +te-chih +te-hsiu +te-wei +teacher +teador +teague +team +teasley +tebbe +tebinka +tec +tech +techih +technica +technoso +teck +tecklenb +ted +tedd +tedda +teddi +teddie +teddy +teder +tedi +tedie +tedman +tedmund +tedra +tedrick +teed +teena +teerdhal +teetwo +teh +tehchi +tehsiu +teichman +teiichi +tein-min +teinmin +teirtza +teitelba +tej +tejada +tejal +tejani +tel +telco +tele +telecom +telesis +telex +telfer +telidis +telke +tello +tellup +telos +telva +temp +temple +temple-d +templeto +ten-huei +tena +tencer +teng +tenhuei +teniola +tenna +tennant +tenney +tennyson +teo +teodoor +teodor +teodora +teodoric +teodoro +tera +terakado +teran +terangue +terence +terencio +teresa +terese +teresina +teresita +teressa +terez +teri +teriann +terminal +terneus +terr +terra +terrade +terrance +terranel +terranov +terrel +terrell +terrence +terresa +terri +terri-jo +terri-le +terrie +terrijo +terrilei +terrill +terry +terrye +terryl +tersina +teruko +teruo +teruyuki +terwey +terwilli +terza +terzian +tesa +tesch +tesfagab +tesfamar +tesh +tesla +tess +tessa +tessi +tessie +tessier +tessler +tessty +tessy +testa +testagc +tester +testing +testingp +testntmv +testsds +testtool +tetrault +tetreaul +tetsukaz +tetsumo +tetsuo +tetsuya +tetsuyuk +teufel +tevlin +tewksbur +tex +teymour +thabet +thac +thach +thacher +thacker +thad +thaddeus +thaddus +thadeus +thai +thain +thaine +thais +thaker +thakor +thakur +thaler +thalia +tham +thames +thane +thang +thanh +thanh-ha +thanh-ho +thanh-hu +thanh-qu +thanh-so +thanh-ti +thanos +thao +tharby +tharring +thatch +thatcher +thatte +thaxter +thaxton +thayer +thayne +the +the worl +thea +theadora +thebault +theda +thedford +thedora +thedric +thedrick +thege +theis +thekla +thelma +theloose +themann +theo +theobald +theochar +theodor +theodora +theodore +theodori +theodosi +theofani +theohari +theologo +theoret +thera +theresa +therese +theresin +theresit +theressa +theriaul +therien +therine +theriot +theron +therrien +thersa +thevenar +thewalt +thi +thi-cuc +thia +thibaud +thibault +thibaut +thibeaul +thibert +thibodea +thibon +thiebaut +thieken +thiel +thiem +thien +thierry +thiery +thies +thiessen +thieu +thifault +thill +thimothy +thinh +think +thirugna +this dir +thisdel +thisner +thoai +thoi +thom +thoma +thomaier +thomalla +thomas +thomasa +thomasi +thomasia +thomasin +thomason +thomasse +thomassi +thombors +thomey +thomlins +thompson +thoms +thomsen +thomson +thon +thondanu +thor +thoreau +thorin +thorley +thorman +thorn +thornber +thornbur +thorndik +thorne +thornie +thornley +thornton +thorny +thorpe +thorsen +thorslun +thorson +thorstei +thorsten +thorvald +those +threader +thrift +throgmor +thu +thuan +thuesen +thum +thumm +thuong +thurgood +thurley +thurman +thurstan +thurston +thuswald +thuthuy +thuy +thyagara +ti +ti-cheng +ti-jeun +tian +tianbao +tiberghi +tibi +tibold +tibor +tice +ticheng +ticzon +tidball +tidd +tidwell +tiebold +tiebout +tiedeman +tiefenth +tiegs +tien +tien-bue +tien-chi +tiena +tienbuen +tienchie +tienyow +tierney +tiertza +tieu +tiff +tiffani +tiffanie +tiffany +tiffi +tiffie +tiffy +tigg +tigger +tigran +tihanyi +tiina +tijeun +tilak +tilbenny +tilda +tilden +tildi +tildie +tildy +tiler +tilk +tille +tiller +tilley +tillie +tillman +tilly +tilmon +tilson +tilton +tim +timeshee +timi +timleck +timler +timm +timmer +timmerma +timmi +timmie +timmins +timmons +timms +timmy +timo +timofei +timos +timoteo +timothea +timothee +timotheu +timothy +timpson +tims +timsit +timtsche +tin +tina +tine +tineke +tiner +ting +ting-shu +ting-tin +ting-yu +tingshuo +tingting +tingyu +tingyue +tini +tinney +tino +tintor +tiny +tio +tiong-ho +tip +tiphani +tiphanie +tiphany +tippett +tipping +tippy +tipton +tirrell +tischhau +tischler +tisdale +tisdall +tiseo +tish +tisha +titian +tito +titos +titus +tiu +tiwari +tiziano +tjahjadi +tjia +tjiong +tjoe +tjong +toan +toastmas +toba +tobe +tobey +tobi +tobiah +tobias +tobie +tobin +tobit +toby +tobye +tod +todaro +todd +toddi +toddie +toddy +todloski +todo +todorovi +toerless +toews +toft +togasaki +tognoni +tohama +toi +toiboid +toinette +tolar +toles +toletzka +tolgyess +tolle +tollefse +tolson +tolstoy +toly +tom +toma +tomacic +tomack +tomacruz +tomar +tomas +tomasett +tomasina +tomasine +tomaso +tomassi +tomasz +tomaszew +tombul +tome +tomes +tomi +tomkin +tomlin +tomlinso +tommaso +tommi +tommie +tommy +tomochek +tomohiro +tomoyosh +tompkins +tomy +tonelli +toney +tong +tongder +toni +tonia +tonie +tonkovic +tonnie +tonny +tonogai +tonu +tony +tonya +tonye +tookey +toolbox +toole +tooley +toolroom +tools +toolset +toomer +toone +toop +toothman +tootsie +tooyserk +toperzer +topgun +toplis +topo +topol +topp +tor +torain +torbert +torcac +tordocs +tore +torey +tori +torian +torie +toril +torin +tornes +tornqvis +torok +torr +torrance +torre +torrealb +torrell +torrence +torrens +torres +torrey +torrie +torrin +torry +torsten +torunn +tory +tosca +toscano +tosczak +toshach +toshachn +toshi +toshiaki +toshihir +toshinar +toss +tostenso +tota +totaro +toth +totino +totman +totten +totti +touati +touchett +toufic +tougas +toulson +toupin +tousigna +toussain +tova +tovah +tove +towaij +towers +towill +towler +towles +town +towney +townie +townley +towns +townsel +townsend +townson +towny +towsley +toyanne +toyoji +toyooka +tprl +trace +tracee +traces +tracey +tracey-m +trachsel +traci +tracie +tracy +tracz +trader +trae +trafford +trahan +trainer +training +trainor +trajan +tramar +trame +tran +tranfagl +trang +transki +translat +tranter +trasmund +traugott +traulich +traut +trautman +travel-p +travelpe +traver +travers +travis +travus +traxler +trayer +traylor +traynor +trecia +tredenni +tredway +treen +trees +trefor +trefry +trefts +tregenza +treisman +trek @ +tremain +tremaine +tremayne +tremblay +tremewan +trenna +trent +trentadu +trenton +tres +tres-sup +tresa +trescha +trese +tresrch +tressa +trev +trevar +trever +trevetha +trevitt +trevor +trey +tri +trial +triantap +tricci +tricia +trickett +tricord +trieu +trif +trifiro +triggian +trijanto +trimble +trina +trindy +trinh +trinidad +trink +trip +tripier +tripleho +tripp +tripps +tris +trish +trisha +trisic +trisko +trista +tristam +tristan +tristano +trittler +tritton +trivedi +trix +trixi +trixie +trixy +trocchi +trochu +troesch +trojak +tromm +trong +tropea +tropeano +trotsky +trotter +trottier +troubors +troup +trowbrid +troy +troyvoi +trpisovs +trstram +tru-fu +truchon +truda +trude +trudel +trudell +trudey +trudi +trudie +trudy +truebloo +truelove +trueman +truesdal +truffer +trujillo +trula +trull +truls +trumaine +truman +trumann +trumble +trung +trungy +trunley +truong +truran +trussler +trutsche +truus +tryfon +trying +tsai +tsai-hun +tsaihung +tsakalis +tsalikis +tsang +tsao +tsay +tschaja +tschann +tse +tse-lian +tseliang +tseng +tsenter +tsern +tsiakas +tsing +tsitsior +tsitsons +tso +tsolas +tsong-li +tsonglia +tsonos +tsortos +tsoucas +tsugio +tsui +tsuji +tsuk +tsun-kuo +tsun-yuk +tsuneo +tsung +tsunkuo +tsunoda +tsunyuk +tsuyoshi +ttisuppo +tu +tuan +tubb +tuck +tucker +tuckie +tucky +tudo +tudor +tue +tuen +tuesday +tuffo +tufford +tuhina +tuhr +tulga +tulio +tulip +tulk +tull +tulley +tullius +tullo +tully +tun-lin +tuna +tunali +tung +tung-min +tunghsin +tungming +tunon +tuoi +tuok +tuong +tupas +tupling +turbes +turbyfil +turchan +turcot +turcotte +turgay +turing +turkeer +turkey +turki +turkki +turkovic +turnbull +turner +turney +turpin +turrubia +turunen +turus +tushar +tussey +tusting +tutt +tuttle +tuxford +tuyen +tuyetphu +twa +twana +twarog +tweddle +tweetie +twidale +twiggy +twila +twiss +twitty +twolan +twx +twyla +twyman +twynham +twyver +txp +ty +tyack +tybalt +tybi +tybie +tyda +tye +tyke +tyler +tymchuk +tymon +tymothy +tynan +tyndall +tyne +tyner +typer +tyra +tyrance +tyroler +tyron +tyronda +tyrone +tyrrell +tyrus +tyson +tzanetea +tzeng +tzong-sh +tzong-ya +tzongshi +tzongyan +tzou +tzuang +tzung +uae +uathavik +uberig +uchida +uchiyama +udale +udall +uday +udaya +udayasek +ude +udell +uecker +ueda +uehara +ueyama +uffner +ufomadu +ugo +uguccion +ugwa +uhl +uhley +uhlhorn +uhlig +ukena +ula +ulberto +ulf +ulgen +uli +ulick +ulises +ulla +ully +ulric +ulrica +ulrich +ulrick +ulrika +ulrikaum +ulrike +ultrason +uludamar +ulysses +umakanta +umakanth +umberto +umeeda +umeh +umeko +umesh +umetsu +umphres +una +una-mae +unabr.di +underwoo +unger +unitt +unix +unixsupp +unkefer +unkles +unreg +unsoy +unxlb +upchurch +updt +upen +uppal +upshaw +upton +urata +urbain +urban +urbanic +urbano +urbanowi +urbanus +urbashi +urbick +urbielew +urbshas +uresh +uri +uriah +uriel +urnes +urow +urquhart +urs +ursa +ursala +ursola +urson +ursula +ursulina +ursuline +urwin +us +usa +useng +user +usman +usrouter +uswrsd +usyk +uszynski +uta +utah +utas +utilla +utpal +utpala +utsumi +uunko +uvieghar +uyar +vachel +vacher +vachiran +vachon +vaclav +vaculik +vadala +vadali +vadi +vadim +vafaie +vaglio-l +vahary +vahdat +vahe +vahedi +vahid +vaid +vail +vaillanc +vaillant +vairavan +vajentic +vajih +vakhshoo +vakili +val +valaree +valaria +valcourt +valda +valdemar +valdez +vale +valeda +valencia +valene +valenka +valenta +valente +valentia +valentij +valentik +valentin +valenzia +valera +valeria +valerie +valerien +valerio +valerius +valery +valerye +valia +valida +valin +valina +valinda +valiquet +valiveti +valko +valkyrie +valla +vallath +valle +vallee +vallejos +vallenty +vallet +valli +valliani +vallie +vallier +valliere +vallipur +vallozzi +vally +valma +valois +valorie +valry +valvasor +van +van alph +van alst +van atta +van bake +van bent +van coon +van den +van der +van dyke +van es +van eyk +van flee +van gaal +van hast +van hols +van hoy +van huls +van kast +van kess +van klin +van late +van leeu +van loon +van mans +van mete +van nest +van oors +van orde +van phil +van rijn +van rijs +van scho +van schy +van sick +van terr +van veen +van vrou +van weri +van-king +vanaman +vanasse +vance +vanda +vandagri +vandenbe +vandenbo +vandenhe +vandenki +vanderbi +vanderbo +vanderbu +vanderge +vanderhe +vanderho +vanderpo +vanderve +vanderwe +vandeval +vandevan +vandeven +vandewat +vandewou +vandomme +vandoorn +vandusen +vanessa +vangaste +vania +vanity +vankoote +vanlaar +vanliew +vann +vanna +vanni +vannie +vanny +vanpatte +vanstaal +vanstory +vanta +vanter +vanwormh +vanwyche +vanya +vanzella +varady +varaiya +varano +varda +vardy +varennes +vargas +vargo +varia +varkel +varkey +varley +varmazis +varsava +vartanes +varughes +varujan +vasan +vasantha +vasarhel +vaserfir +vasil +vasile +vasili +vasiliad +vasiliki +vasilis +vasilopo +vasily +vason +vasoufz +vassili +vassilik +vassilis +vassily +vassos +vastine +vasu +vasudeva +vaswani +vaterlau +vaters +vaughan +vaughn +vavarout +vavroch +vawter +vax +vazirani +vea +veale +veals +veciana +veck +ved +veda +vedant +veedell +veen +veena +vega +vehling +veillett +veilleux +veit +vejar +veklerov +veksler +vela +velasque +vele +veleta +velez +veljko +vella +velline +vellino +velma +veloria +veloz +velsher +velvet +vempati +ven +venbakm +vendette +veneice +veness +veng +venger +venguswa +venier +venita +venjohn +venkat +venkata +venkatak +venkatar +venkates +venkatra +venne +venner +venning +vennos +ventrone +ventura +venturin +venus +vera +veradis +verardi +verch +verde +verdi +verdonse +vere +verena +verene +verge +verghese +vergil +verheyde +verhoeve +verhotz +veriee +verifica +verile +verina +verinder +verine +verkroos +verla +verlyn +verma +vermeesc +vermette +vern +verna +verne +vernen +verney +vernice +verniece +vernita +vernon +vernor +verona +veronica +veronika +veronike +veroniqu +verreau +verrenne +verrilli +versace +versteeg +vertolli +verville +veryl +verzilli +veselko +vesna +vespa +vester +vesterda +vetil +vetrano +vetrie +vetter +vettese +vevay +vexler +vey +veyrat +vezeau +vezina +vi +viano +viau +viavant +vibeke +vic +vice +vicente +vicheara +vick +vickers +vicki +vickie +vicky +victoir +victor +victoria +vicuong +vida +vidaurri +videa +vidhyana +vidmer +vidovic +vidovik +viduya +vidya +viegas +vieger +viehweg +vieillar +vieira +vieiro +viens +viera +vieregge +vigeant +viitanie +vijai +vijay +vijaya +vijayala +vijya +vik +vikas +viki +vikki +vikky +vikram +vikrant +viktor +viktoria +vilas +vilayil +vilhan +vilhelm +vilhelmi +vilis +villanue +villarea +villella +villeneu +vilma +vilmanse +vilok +vilozny +vimal +vimi +vin +vina +vinas +vinay +vince +vincent +vincente +vincents +vincenty +vincenz +vincenzo +vineet +vinet +viney +vinh +vinita +vinnell +vinni +vinnie +vinny +vino +vinod +vinson +viola +violante +viole +violet +violeta +violetta +violette +vipi +vipul +viqar +virani +virant +virchick +virge +virgie +virgil +virgilio +virgina +virginia +virginie +virgoe +viriato +viriya +virk +virko +visentin +vish +vishal +vishwa +visiting +viskanta +visockis +vispi +vispy +visser +vistlik +visvanat +viswa +viswamit +vita +vitacco +vitaglia +vital +vite +vithit +vitia +vito +vitoria +vittoria +vittorio +viv +viva +viveca +vivek +vivi +vivia +vivian +viviana +viviane +vivianna +vivianne +vivie +vivien +viviene +vivienne +vivier +viviyan +vivyan +vivyanne +vlad +vladamir +vladdy +vladica +vladimir +vladisla +vlado +vlahos +vlanin +vm +vmbackup +vmchange +vmcord +vmsuppor +vmxa +vo +voadmin +vodicka +voduc +voelcker +vogel +vogt +voight +voitel +volchegu +volfe +volk +volker +volkmann +volkmer +vollmer +volz +von +von ende +von semm +von zube +voncanno +vonck +vonderha +vondersc +vonderwe +vonlehmd +vonni +vonnie +vonny +vonreich +vony +vonzant +voort +vopalens +vopni +voros +vosberg +vosburg +voss +vosu +vosup +voula +vowels +vrabel +vradmin +vrbetic +vreugden +vries +vrinda +vrouwerf +vu +vucinich +vuhoan +vuignier +vuncanno +vuong +vuquoc +vyachesl +vyaragav +vyas +vyjayant +vyky +vyza +wa +waals +wacheski +wachtste +wacker +wada +wadasing +waddell +wadden +waddick +waddingt +wade +wadkins +wadswort +waespe +waeyen +wagage +wager +wagers +waggoner +waghorne +waghray +wagle +wagner +wagoner +wahab +wahbe +wahju +wai +wai-bun +wai-chau +wai-chin +wai-fah +wai-hung +wai-leun +wai-man +waichi +waid +waidler +waifah +waigh +waihung +wain +waines +wainwrig +waissman +wait +waite +waiter +waitman +waja +wakabaya +wakako +wake +wakefiel +wakeham +wakim +walas +walbridg +walburga +walchli +wald +waldemar +walden +waldick +waldie +waldo +waldon +waldron +wales +waletzky +walford +walia +walid +walker +walkins +walkley +walkowia +wallace +wallache +wallaert +wallas +wallbank +waller +walles +walley +wallgren +wallie +wallis +walliw +walls +wally +waloff +walpole +walrand +walrond +walser +walsh +walston +walt +walta +waltdisn +walter +walters +walther +walton +waltraud +waly +walz +wambsgan +wamozart +wan +wanda +wandel +wandie +wandis +wandojo +wandsche +waneta +wang +wanids +wannell +wanner +wans +wanzeck +war +warburg +warburto +ward +warde +warden +wardle +wardrop +ware +wares +warfel +wargnier +warin +waring +wark +warkenti +warner +warnock +warnow +warrello +warren +warriner +warshaws +wartman +warun +warwick +waschuk +waserman +wash +washburn +washingt +wasim +wasitova +wasley +wasmeier +wassel +wasserma +wassim +wasson +wasylenk +wasylyk +wat +watanabe +watchmak +watchorn +waterhou +waterman +waters +watford +watkins +watkinso +watmore +watson +watters +wattier +watts +watznaue +waucheul +waugh +waverley +waverly +way +waybrigh +wayez +waylan +wayland +waylen +wayler +waylin +wayling +waylon +wayman +waymon +wayne +waytowic +weagle +weakley +wealch +weare +wease +weatherl +weathers +weaver +web +webb +webber +weber +webster +weckwert +weddell +wee-lin +wee-seng +wee-thon +weedmark +weeks +wefald +wefers +wegener +weger +wegner +wegrowic +wehara +wei +wei-i +wei-kun +wei-tsig +wei-yih +weibust +weicheng +weichung +weidar +weidenbo +weidenfe +weider +weidinge +weidner +weiguang +weiheng +weihs +weihsing +weii +weijia +weijie +weikang +weikuang +weikun +weil +weilin +weimin +weimong +weinbend +weinberg +weiner +weingart +weinkauf +weiping +weirich +weisenbe +weiser +weiss +weist +weitsig +weitz +weitzel +weiyih +welbie +welby +welch +weldon +welham +welker +wellard +welling +wells +wellstoo +welsch +welsford +welsh +welten +wemple +wen +wen-chie +wen-hann +wen-juin +wen-kai +wen-lian +wen-miin +wen-shan +wenbin +wenchien +wenchih +wenda +wendall +wendel +wendelin +wendell +wendi +wendi-st +wendie +wendista +wendling +wendong +wendt +wendy +wendye +weng +wenham +wenhann +wenjuin +wenliang +wenmiin +wennan +wennerst +wenona +wenonah +wensel +wenshan +wensley +wentwort +wentzcov +wenxi +wenyon +wenzel +wepf +weppler +werewolf +werick +weringh +werling +werner +wernher +wernik +werth +wertz +wery +wes +wesenber +wesley +wesolosk +wesolows +wessel +wessell +wesselma +wesselow +wessels +wessenbe +west +westbroo +westcott +wester +westfall +westgart +westlake +westleig +westley +westmore +weston +weston-d +westphal +westwood +wetherbe +wettelan +wetzel +wever +weyand +weylin +wga +whalen +whaley +whang +whatley +wheatley +wheaton +wheeler +wheelock +whei-may +wheimay +whelan +whelpdal +whetston +whetzel +whey +whey-min +wheyming +whidden +whinnery +whipple +whipps +whirpool +whirter +whisenhu +whiskin +whisler +whit +whitaker +whitby +whitcomb +whited +whitefor +whitehur +whiteman +whitesid +whitfiel +whitfill +whitford +whiting +whitlock +whitman +whitmore +whitney +whitsell +whitt +whittake +whittam +whitten +whittier +whitting +whitton +whitty +whitwam +whitwell +whitwort +whoi +whyte +wiatt +wichers +wichman +wicht +wichterl +wickes +wickham +wickie +widdicom +widdis +widdowso +widener +widianto +widows +widrig +widuch +wiebe +wiebren +wiederho +wiedman +wiedmann +wiegand +wieland +wiele +wienert +wiens +wiercioc +wierzba +wieser +wiesje +wieslaw +wieslawa +wiest +wigderso +wiggin +wiggins +wiggs +wight +wigle +wignall +wikkerin +wiklund +wil +wilbert +wilbur +wilburt +wilby +wilcox +wilczews +wilde +wildeman +wilden +wilder +wilderma +wildgen +wildman +wildon +wileen +wilek +wilemon +wilen +wilenius +wilensky +wiley +wilf +wilford +wilfred +wilfrid +wilgosh +wilhelm +wilhelmi +wilhelms +wilhelmu +wilhoit +wilie +wilke +wilken +wilkerso +wilkes +wilkie +wilkin +wilkins +wilkinso +wilko +wilks +will +willa +willabel +willamin +willard +willcock +willcox +willdon +willeke +willekes +willem +willemij +willemse +willenbr +willets +willett +willetta +willette +willey +willhoff +willi +william +williams +willie +willifor +willis +willison +willmore +willmott +willough +willow +willson +willy +willyt +wilma +wilmar +wilmer +wilmette +wilmont +wilmore +wilnai +wilona +wilone +wilow +wilsey +wilson +wilt +wilton +wiltz +wimberle +wimbush +wimmer +win +win-chyi +wina +winchest +winchyi +winde +windham +windom +windowin +windsor +windy +winerman +winfield +winfred +wing +wing-ki +wing-man +wing-tai +wingar +wingard +wingate +wingfiel +wingo +wingrove +wingtai +wini +winicki +winifiel +winifred +winje +winklema +winkler +winlow +winn +winna +winnah +winne +winni +winnie +winnifre +winningh +winningt +winnipeg +winny +winona +winonah +winsberg +winsborr +winsky +winslow +winstead +winston +winterbe +winters +winthrop +wintour +wippel +wiring +wirth +wiseman +wishewan +wisniews +wissinge +wissler +wit +witchlow +witham +withrow +witkowsk +witney +witold +witort +wits +witt +witte +wittich +wittie +wittik +wittman +witty +witzel +witzman +witzmann +wladysla +woan +wobbrock +woei-pen +woelffel +woessner +woinsky +wojciech +wojcik +wojdylo +wojnar +wojtecki +wokoma +wolczans +wolf +wolfe +wolfenba +wolff +wolfgang +wolfie +wolfman +wolford +wolfs +wolfson +wolfy +wolk +woll +woloshko +wolowidn +wolska +wolski +wolter +womack +womble +won +won-uk +wonda +wong +wonuk +wood +woodall +woodford +woodhall +woodie +woodley +woodlief +woodline +woodman +woodrow +woods +woodson +woodward +woody +woodyer +wooff +woojin +wook +wookie +woolery +wooley +woollam +woolley +woolwine +woon +wooster +wooten +wooters +wootton +worden +words fr +words in +working +world.fa +wormald +worms +worobey +woroszcz +worpell +worrall +worsley +worth +worthing +worthy +wortman +wozniak +wpms +wracher +wragg +wray +wren +wrennie +wriggles +wright +wrigley +writing +wrobel +wroblews +wruck +wsadmin +wsbackup +wu +wuan +wueppelm +wuertele +wun +wunderli +wurtz +wyant +wyatan +wyatt +wyble +wycoff +wydra +wye +wykoff +wylie +wyllie +wylma +wylo +wyman +wymard +wyn +wyndham +wynes +wynn +wynne +wynnie +wynny +wyrstiuk +wyss +wytenbur +wyzga-ta +xantippe +xavier +xaviera +xayaraj +xena +xenia +xenophon +xenos +xerxes +xever +xi-nam +xi-xian +xian +xiang-se +xiangsen +xianjie +xiao +xiao-min +xiaobing +xiaofei +xiaofeng +xiaoguan +xiaohui +xiaojing +xiaolei +xiaolin +xiaolong +xiaomei +xiaoping +xiaowen +xiaoxia +xie +xila +ximenes +ximenez +xin +xingchao +xingdong +xinlin +xinyi +xiong +xiqing +xixian +xmssuppo +xnew +xongxong +xpm +xpmbld +xpmbuild +xu +xuan +xuan-lie +xuefeng +xueling +xumin +xuong +xylia +xylina +xymenes +ya-shu +yabe +yach +yadollah +yaeger +yael +yafa +yaghutie +yahia +yahyapou +yakibchu +yakimovi +yakir +yalcin +yale +yali +yalonda +yamada +yamamoto +yamaoka +yamashit +yamato +yamaura +yamin +yan +yan-shek +yan-zhen +yanagida +yanan +yanaton +yance +yancey +yancy +yandell +yanjun +yank +yankee +yann +yanna +yannick +yannis +yano +yanosik +yanshek +yansun +yao +yao-nan +yaonan +yaphet +yaping +yarber +yarbroug +yard +yardley +yardy +yarlanda +yarnell +yaron +yarosh +yaroslav +yasar +yaser +yashu +yasmeen +yasmin +yassa +yassar +yassin +yasuaki +yasuhiro +yasuko +yasumasa +yasuo +yasushi +yasuura +yate +yates +yatin +yatish +yau +yau-fun +yau-mun +yau-wu +yaumun +yaung +yauwu +yavar +yavuz +yawar +yazdani +yazdi +yc +ye-sho +yea-ping +yeager +yeal +yeaping +yearwood +yeaton +yechezke +yeck +yedema +yee +yee-ning +yeh +yehuda +yehudi +yehudit +yeirnie +yelena +yelvingt +yemuna +yen +yen-heng +yen-jhy +yen-meng +yendall +yeng +yenheng +yenilmez +yenjhy +yenmeng +yenor +yeo +yeo-hoon +yeocheol +yeohoon +yeong-ch +yeong-eo +yeongchy +yeongeon +yerga +yerigan +yerneni +yesho +yetta +yettie +yetty +yetung +yeun +yeun-jyr +yeung +yeunjyr +yevette +yew-shin +yewshing +yezheng +yezi +yhu-tin +yhutin +yi +yi-min +yiannis +yie-tarn +yietarng +yifei +yigal +yih +yihban +yihchih +yii-mei +yiimei +yijean +yikhon +yiliang +yim +yimin +ying +ying-cdi +yingcdi +yishun +yitan +yiu-kong +yiukong +yixia +yixin +ylaine +yll-chen +yllcheng +ynes +ynez +yngvar +yoakum +yock +yoda +yodha +yoe +yogesh +yogeswar +yogi +yohe +yokan +yoke +yoke-kee +yokeley +yoko +yokono +yokoono +yolanda +yolande +yolane +yolanthe +yon-chun +yonchun +yong +yong-hyu +yongdong +yonghyun +yongil +yongli +yongxin +yonhong +yonik +yonk +yoo +yoon +yoon-mo +yoonjung +yoonmo +yoonsik +yoram +yorgo +yorgos +york +yorke +yorker +yoshi +yoshiaki +yoshihit +yoshikaw +yoshiko +yoshimi +yoshimit +yoshinob +yoshio +yoshioka +yoshiyam +yosi +yossaria +yost +yosuf +you-lian +youel +youji +youliang +youlin +youn +youn-jun +younan +younes +young +young-ba +young-il +young-ju +youngbai +youngblo +younger +youngill +younglov +youngman +youngqui +youngs +younjung +younkin +yount +youping +yousef +yousefpo +youssef +yousuf +youwen +yovonnda +yowell +ysabel +ytshak +yu +yu-chen +yu-chian +yu-chung +yu-hung +yu-kai +yu-pei +yu-wei +yuan +yuan-cha +yuan-shi +yuanchao +yuanjian +yuanshin +yuchen +yuchiang +yuchong +yudin +yudy +yue +yue-min +yue-shun +yuechu +yueh +yueh-min +yueh-shi +yuehming +yuehshio +yuehwern +yueli +yuen +yuen-pui +yuenglin +yueping +yueshun +yugang +yuh-dauh +yuh-jiun +yuh-tai +yuhanna +yuhdauh +yuhjiun +yuhn +yuhtai +yuill +yuji +yujie +yuk-wha +yuke +yukihiko +yukiko +yukinaga +yukinobu +yuklung +yuko +yuksel +yukuo +yul +yule +yulia +yulma +yum +yuma +yumi +yumurtac +yun +yun-sun +yundt +yung +yung-chi +yung-chu +yung-fu +yung-pin +yung-yu +yungchia +yungchun +yungfu +yunghuoy +yungmuh +yungping +yungyu +yunn-tzu +yunntzu +yunsun +yuon-kua +yuonkuan +yupei +yupin +yurach +yurchuk +yuri +yurik +yussuf +yuste +yutaka +yuting +yuval +yuwei +yuyi +yuyu +yvan +yves +yvet +yvette +yvon +yvonne +yvor +yzerman +z-80 +z80 +zabek +zabokrzy +zabransk +zabrina +zaccari +zaccaria +zach +zacharia +zacharie +zachary +zacherie +zachery +zack +zackaria +zadeh +zadorozn +zadow +zafar +zafarano +zafarull +zafer +zaga +zagorsek +zagorski +zagrodne +zahara +zaharoff +zaharych +zahid +zahir +zahirul +zahn +zahnley +zahra +zaia +zaid +zaidi +zaihua +zainab +zajac +zak +zaka +zakai +zakarow +zaker +zalameda +zalcstei +zalee +zaleski +zalite +zaliznya +zalman +zalokar +zaloker +zalzale +zaman +zampino +zan +zanariah +zander +zandra +zane +zanet +zaneta +zanetti +zanga +zani +zanni +zantiris +zapach +zappe +zara +zaragoza +zarah +zarate +zared +zarella +zaretsky +zargham +zaria +zarkel +zarla +zarlenga +zarrabia +zarrin +zatkovic +zatti +zattiero +zatylny +zauhar +zauner +zavadiuk +zaven +zawadka +zaydan +zazulak +zbib +zbignew +zbigniew +zbuda +zdenek +zdenka +zdenko +zea +zeb +zebadiah +zebedee +zebulen +zebulon +zecharia +zed +zedekiah +zedrick +zee +zeggil +zegray +zehir-ch +zehra +zeidler +zeiger +zeigler +zeilinge +zeimet +zein +zeina +zeisler +zeitler +zejing +zeke +zelda +zelenka +zelig +zeljko +zelko +zeller +zellers +zelma +zelsmann +zelwer +zemanek +zen +zena +zenaida +zenar +zeng +zenghong +zenia +zenisek +zenkevic +zenkner +zenon +zere +zerk +zero +zerriffi +zetterlu +zetts +zexiang +zhang +zhanna +zhao +zhaohong +zhaoqi +zhaoxu +zhelka +zhen +zheng +zhengyu +zhilan +zhishun +zhiwei +zhixin +zhiyong +zhong +zhongde +zhongfu +zhongjin +zhongqua +zhongxia +zhou +zhuezhi +zhuolin +zi-ping +zi-qiang +zia +ziad +ziai +zicheng +ziebarth +zieber +ziegler +ziehn +zielinsk +ziemba +zigrand +zilaie +zilberst +zilla +zilvia +zimmer +zimmerer +zimmerly +zimmerma +zina +zinati +zingale +zingeler +zinkie +zinn +zino +ziomek +zipcodes +ziping +zippora +ziqiang +zirko +zissis +zisu +zita +zitella +zitko +zito +zitzmann +ziva +zivanovi +zivilik +zivkovic +ziyi +ziyou +zlatin +zlotnick +znack +zoe +zoehner +zoel +zoellner +zoenka +zoerb +zofia +zohair +zohar +zohman +zohreh +zola +zollie +zollman +zolly +zolmer +zoltan +zonda +zondra +zone-chi +zonechin +zongyi +zonker +zonner +zonnya +zonoun +zoppel +zora +zorah +zoran +zorana +zoratti +zorina +zorine +zork +zorn +zorony +zorzi +zottola +zou +zouheir +zrobok +zsa zsa +zsazsa +zubair +zubans +zuben +zubricki +zuccarel +zuckerma +zug +zuhua +zuk +zukas +zukosky +zukovsky +zulema +zulfikar +zumel +zumhagen +zumpf +zunuzi +zuranato +zurawlev +zureik +zurl +zuzana +zvonar +zwi +zwick +zwicker +zwierzch +zybala +zyg +zygmunt +zylstra +zywiel \ No newline at end of file diff --git a/examples/crud_rest_api/README.rst b/examples/crud_rest_api/README.rst new file mode 100644 index 000000000..ed29b8980 --- /dev/null +++ b/examples/crud_rest_api/README.rst @@ -0,0 +1,18 @@ +Quick How to Example On REST API +-------------------------------- + +Simple contacts application. + +Create an Admin user:: + + $ fabmanager create-admin + +Insert test data:: + + $ python testdata.py + +Run it:: + + $ fabmanager run + + diff --git a/examples/crud_rest_api/app/__init__.py b/examples/crud_rest_api/app/__init__.py new file mode 100644 index 000000000..9123de75c --- /dev/null +++ b/examples/crud_rest_api/app/__init__.py @@ -0,0 +1,28 @@ +import logging + +from flask import Flask + +from flask_appbuilder import SQLA, AppBuilder + +#from sqlalchemy.engine import Engine +#from sqlalchemy import event + +logging.basicConfig(format='%(asctime)s:%(levelname)s:%(name)s:%(message)s') +logging.getLogger().setLevel(logging.DEBUG) + +app = Flask(__name__) +app.config.from_object('config') +db = SQLA(app) +appbuilder = AppBuilder(app, db.session) + +""" +Only include this for SQLLite constraints + +@event.listens_for(Engine, "connect") +def set_sqlite_pragma(dbapi_connection, connection_record): + cursor = dbapi_connection.cursor() + cursor.execute("PRAGMA foreign_keys=ON") + cursor.close() +""" + +from . import models, api diff --git a/examples/crud_rest_api/app/api.py b/examples/crud_rest_api/app/api.py new file mode 100644 index 000000000..ac03447fa --- /dev/null +++ b/examples/crud_rest_api/app/api.py @@ -0,0 +1,35 @@ +from flask_appbuilder import ModelRestApi +from flask_appbuilder.models.sqla.interface import SQLAInterface +from . import db, appbuilder +from .models import ContactGroup, Gender, Contact + + +def fill_gender(): + try: + db.session.add(Gender(name='Male')) + db.session.add(Gender(name='Female')) + db.session.commit() + except: + db.session.rollback() + + +db.create_all() +fill_gender() + + +class ContactModelApi(ModelRestApi): + resource_name = 'contact' + datamodel = SQLAInterface(Contact) + allow_browser_login = True + + +appbuilder.add_api(ContactModelApi) + + +class GroupModelApi(ModelRestApi): + resource_name = 'group' + datamodel = SQLAInterface(ContactGroup) + allow_browser_login = True + + +appbuilder.add_api(GroupModelApi) diff --git a/examples/crud_rest_api/app/models.py b/examples/crud_rest_api/app/models.py new file mode 100644 index 000000000..5c2c61600 --- /dev/null +++ b/examples/crud_rest_api/app/models.py @@ -0,0 +1,47 @@ +import datetime +from sqlalchemy import Column, Integer, String, ForeignKey, Date +from sqlalchemy.orm import relationship +from flask_appbuilder import Model + +mindate = datetime.date(datetime.MINYEAR, 1, 1) + + +class ContactGroup(Model): + id = Column(Integer, primary_key=True) + name = Column(String(50), unique=True, nullable=False) + + def __repr__(self): + return self.name + + +class Gender(Model): + id = Column(Integer, primary_key=True) + name = Column(String(50), unique=True, nullable=False) + + def __repr__(self): + return self.name + + +class Contact(Model): + id = Column(Integer, primary_key=True) + name = Column(String(150), unique=True, nullable=False) + address = Column(String(564)) + birthday = Column(Date, nullable=True) + personal_phone = Column(String(20)) + personal_celphone = Column(String(20)) + contact_group_id = Column(Integer, ForeignKey('contact_group.id'), nullable=False) + contact_group = relationship("ContactGroup") + gender_id = Column(Integer, ForeignKey('gender.id'), nullable=False) + gender = relationship("Gender") + + def __repr__(self): + return self.name + + def month_year(self): + date = self.birthday or mindate + return datetime.datetime(date.year, date.month, 1) or mindate + + def year(self): + date = self.birthday or mindate + return datetime.datetime(date.year, 1, 1) + diff --git a/examples/crud_rest_api/app/translations/pt/LC_MESSAGES/messages.mo b/examples/crud_rest_api/app/translations/pt/LC_MESSAGES/messages.mo new file mode 100644 index 000000000..7713f481f Binary files /dev/null and b/examples/crud_rest_api/app/translations/pt/LC_MESSAGES/messages.mo differ diff --git a/examples/crud_rest_api/app/translations/pt/LC_MESSAGES/messages.po b/examples/crud_rest_api/app/translations/pt/LC_MESSAGES/messages.po new file mode 100644 index 000000000..3299f7099 --- /dev/null +++ b/examples/crud_rest_api/app/translations/pt/LC_MESSAGES/messages.po @@ -0,0 +1,27 @@ +# Portuguese translations for PROJECT. +# Copyright (C) 2014 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2014-01-13 00:29+0000\n" +"PO-Revision-Date: 2014-01-13 00:18+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: pt \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 1.3\n" + +#: app/views.py:42 +msgid "List Groups" +msgstr "Lista de Grupos" + +#: app/views.py:43 +msgid "List Contacts" +msgstr "" + diff --git a/examples/crud_rest_api/babel/babel.cfg b/examples/crud_rest_api/babel/babel.cfg new file mode 100644 index 000000000..70e23ac63 --- /dev/null +++ b/examples/crud_rest_api/babel/babel.cfg @@ -0,0 +1,3 @@ +[python: **.py] +[jinja2: **/templates/**.html] +encoding = utf-8 diff --git a/examples/crud_rest_api/babel/messages.pot b/examples/crud_rest_api/babel/messages.pot new file mode 100644 index 000000000..2e5a6fb97 --- /dev/null +++ b/examples/crud_rest_api/babel/messages.pot @@ -0,0 +1,27 @@ +# Translations template for PROJECT. +# Copyright (C) 2014 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2014. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2014-01-13 00:29+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 1.3\n" + +#: app/views.py:42 +msgid "List Groups" +msgstr "" + +#: app/views.py:43 +msgid "List Contacts" +msgstr "" + diff --git a/examples/crud_rest_api/config.py b/examples/crud_rest_api/config.py new file mode 100644 index 000000000..bcc56549f --- /dev/null +++ b/examples/crud_rest_api/config.py @@ -0,0 +1,69 @@ +import os + +basedir = os.path.abspath(os.path.dirname(__file__)) + +CSRF_ENABLED = True +SECRET_KEY = '\2\1thisismyscretkey\1\2\e\y\y\h' + +OPENID_PROVIDERS = [ + {'name': 'Google', 'url': 'https://www.google.com/accounts/o8/id'}, + {'name': 'Yahoo', 'url': 'https://me.yahoo.com'}, + {'name': 'AOL', 'url': 'http://openid.aol.com/'}, + {'name': 'Flickr', 'url': 'http://www.flickr.com/'}, + {'name': 'MyOpenID', 'url': 'https://www.myopenid.com'}] + +SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db') +#SQLALCHEMY_DATABASE_URI = 'mysql://username:password@mysqlserver.local/quickhowto' +#SQLALCHEMY_DATABASE_URI = 'postgresql://scott:tiger@localhost:5432/myapp' +#SQLALCHEMY_ECHO = True +SQLALCHEMY_POOL_RECYCLE = 3 + +BABEL_DEFAULT_LOCALE = 'en' +BABEL_DEFAULT_FOLDER = 'translations' +LANGUAGES = { + 'en': {'flag': 'gb', 'name': 'English'}, + 'pt': {'flag': 'pt', 'name': 'Portuguese'}, + 'pt_BR': {'flag':'br', 'name': 'Pt Brazil'}, + 'es': {'flag': 'es', 'name': 'Spanish'}, + 'fr': {'flag': 'fr', 'name': 'French'}, + 'de': {'flag': 'de', 'name': 'German'}, + 'zh': {'flag': 'cn', 'name': 'Chinese'}, + 'ru': {'flag': 'ru', 'name': 'Russian'}, + 'pl': {'flag': 'pl', 'name': 'Polish'}, + 'el': {'flag': 'gr', 'name': 'Greek'}, + 'ja_JP': {'flag': 'jp', 'name': 'Japanese'} +} + + +#------------------------------ +# GLOBALS FOR GENERAL APP's +#------------------------------ +FAB_API_SWAGGER_UI = True + +UPLOAD_FOLDER = basedir + '/app/static/uploads/' +IMG_UPLOAD_FOLDER = basedir + '/app/static/uploads/' +IMG_UPLOAD_URL = '/static/uploads/' +AUTH_TYPE = 1 +#AUTH_LDAP_SERVER = "ldap://dc.domain.net" +AUTH_ROLE_ADMIN = 'Admin' +AUTH_ROLE_PUBLIC = 'Public' +APP_NAME = "F.A.B. Example" +APP_THEME = "" # default +#APP_THEME = "cerulean.css" # COOL +#APP_THEME = "amelia.css" +#APP_THEME = "cosmo.css" +#APP_THEME = "cyborg.css" # COOL +#APP_THEME = "flatly.css" +#APP_THEME = "journal.css" +#APP_THEME = "readable.css" +#APP_THEME = "simplex.css" +#APP_THEME = "slate.css" # COOL +#APP_THEME = "spacelab.css" # NICE +#APP_THEME = "united.css" +#APP_THEME = "darkly.css" +#APP_THEME = "lumen.css" +#APP_THEME = "paper.css" +#APP_THEME = "sandstone.css" +#APP_THEME = "solar.css" +#APP_THEME = "superhero.css" + diff --git a/examples/crud_rest_api/run.py b/examples/crud_rest_api/run.py new file mode 100644 index 000000000..85b107462 --- /dev/null +++ b/examples/crud_rest_api/run.py @@ -0,0 +1,3 @@ +from app import app + +app.run(host='0.0.0.0', port=8080, debug=True) diff --git a/examples/crud_rest_api/testdata.py b/examples/crud_rest_api/testdata.py new file mode 100644 index 000000000..e816126a8 --- /dev/null +++ b/examples/crud_rest_api/testdata.py @@ -0,0 +1,72 @@ +import logging +from app import db +from app.models import ContactGroup, Gender, Contact +import random +from datetime import datetime + +log = logging.getLogger(__name__) + +def get_random_name(names_list, size=1): + name_lst = [names_list[random.randrange(0, len(names_list))].decode("utf-8").capitalize() for i in range(0, size)] + return " ".join(name_lst) + + +try: + db.session.query(Contact).delete() + db.session.query(Gender).delete() + db.session.query(ContactGroup).delete() + db.session.commit() +except: + db.session.rollback() + +try: + groups = [] + groups.append(ContactGroup(name='Friends')) + groups.append(ContactGroup(name='Family')) + groups.append(ContactGroup(name='Work')) + db.session.add(groups[0]) + db.session.add(groups[1]) + db.session.add(groups[2]) + print(groups[0].id) + db.session.commit() +except Exception as e: + log.error("Creating Groups: %s", e) + db.session.rollback() + +try: + genders = list() + genders.append(Gender(name='Male')) + genders.append(Gender(name='Female')) + db.session.add(genders[0]) + db.session.add(genders[1]) + db.session.commit() +except Exception as e: + log.error("Creating Genders: %s", e) + db.session.rollback() + +f = open('NAMES.DIC', "rb") +names_list = [x.strip() for x in f.readlines()] + +f.close() + +for i in range(1, 1000): + c = Contact() + c.name = get_random_name(names_list, random.randrange(2, 6)) + c.address = 'Street ' + names_list[random.randrange(0, len(names_list))].decode("utf-8") + c.personal_phone = random.randrange(1111111, 9999999) + c.personal_celphone = random.randrange(1111111, 9999999) + c.contact_group = groups[random.randrange(0, 3)] + c.gender = genders[random.randrange(0, 2)] + year = random.choice(range(1900, 2012)) + month = random.choice(range(1, 12)) + day = random.choice(range(1, 28)) + c.birthday = datetime(year, month, day) + db.session.add(c) + try: + db.session.commit() + print("inserted", c) + except Exception as e: + log.error("Creating Contact: %s", e) + db.session.rollback() + + diff --git a/examples/quickhowto/app/models.py b/examples/quickhowto/app/models.py index 73d931704..5c2c61600 100644 --- a/examples/quickhowto/app/models.py +++ b/examples/quickhowto/app/models.py @@ -16,7 +16,7 @@ def __repr__(self): class Gender(Model): id = Column(Integer, primary_key=True) - name = Column(String(50), unique = True, nullable=False) + name = Column(String(50), unique=True, nullable=False) def __repr__(self): return self.name @@ -24,7 +24,7 @@ def __repr__(self): class Contact(Model): id = Column(Integer, primary_key=True) - name = Column(String(150), unique = True, nullable=False) + name = Column(String(150), unique=True, nullable=False) address = Column(String(564)) birthday = Column(Date, nullable=True) personal_phone = Column(String(20)) @@ -44,4 +44,4 @@ def month_year(self): def year(self): date = self.birthday or mindate return datetime.datetime(date.year, 1, 1) - + diff --git a/examples/quickhowto/app/views.py b/examples/quickhowto/app/views.py index 2a44df1cd..3c9d58f60 100644 --- a/examples/quickhowto/app/views.py +++ b/examples/quickhowto/app/views.py @@ -3,11 +3,7 @@ from flask_appbuilder.models.sqla.interface import SQLAInterface from flask_appbuilder.charts.views import GroupByChartView from flask_appbuilder.models.group import aggregate_count -from flask_appbuilder.widgets import FormHorizontalWidget, FormInlineWidget, FormVerticalWidget -from flask_babel import lazy_gettext as _ - - -from app import db, appbuilder +from . import db, appbuilder from .models import ContactGroup, Gender, Contact @@ -30,21 +26,33 @@ class ContactModelView(ModelView): ('Summary', {'fields': ['name', 'gender', 'contact_group']}), ( 'Personal Info', - {'fields': ['address', 'birthday', 'personal_phone', 'personal_celphone'], 'expanded': False}), + {'fields': + ['address', 'birthday', 'personal_phone', 'personal_celphone'], + 'expanded': False + } + ), ] add_fieldsets = [ ('Summary', {'fields': ['name', 'gender', 'contact_group']}), ( 'Personal Info', - {'fields': ['address', 'birthday', 'personal_phone', 'personal_celphone'], 'expanded': False}), + {'fields': + ['address', 'birthday', 'personal_phone', 'personal_celphone'], + 'expanded': False + } + ), ] edit_fieldsets = [ ('Summary', {'fields': ['name', 'gender', 'contact_group']}), ( 'Personal Info', - {'fields': ['address', 'birthday', 'personal_phone', 'personal_celphone'], 'expanded': False}), + {'fields': + ['address', 'birthday', 'personal_phone', 'personal_celphone'], + 'expanded': False + } + ), ] @@ -61,12 +69,12 @@ class ContactChartView(GroupByChartView): definitions = [ { - 'group' : 'contact_group', - 'series' : [(aggregate_count,'contact_group')] + 'group': 'contact_group', + 'series': [(aggregate_count, 'contact_group')] }, { - 'group' : 'gender', - 'series' : [(aggregate_count,'contact_group')] + 'group': 'gender', + 'series': [(aggregate_count, 'contact_group')] } ] @@ -74,6 +82,7 @@ class ContactChartView(GroupByChartView): def pretty_month_year(value): return calendar.month_name[value.month] + ' ' + str(value.year) + def pretty_year(value): return str(value.year) @@ -100,8 +109,29 @@ class ContactTimeChartView(GroupByChartView): db.create_all() fill_gender() -appbuilder.add_view(GroupModelView, "List Groups", icon="fa-folder-open-o", category="Contacts", category_icon='fa-envelope') -appbuilder.add_view(ContactModelView, "List Contacts", icon="fa-envelope", category="Contacts") +appbuilder.add_view( + GroupModelView, + "List Groups", + icon="fa-folder-open-o", + category="Contacts", + category_icon='fa-envelope' +) +appbuilder.add_view( + ContactModelView, + "List Contacts", + icon="fa-envelope", + category="Contacts" +) appbuilder.add_separator("Contacts") -appbuilder.add_view(ContactChartView, "Contacts Chart", icon="fa-dashboard", category="Contacts") -appbuilder.add_view(ContactTimeChartView, "Contacts Birth Chart", icon="fa-dashboard", category="Contacts") +appbuilder.add_view( + ContactChartView, + "Contacts Chart", + icon="fa-dashboard", + category="Contacts" +) +appbuilder.add_view( + ContactTimeChartView, + "Contacts Birth Chart", + icon="fa-dashboard", + category="Contacts" +) diff --git a/examples/quickhowto/config.py b/examples/quickhowto/config.py index bba522f78..10755fca4 100644 --- a/examples/quickhowto/config.py +++ b/examples/quickhowto/config.py @@ -34,7 +34,7 @@ 'ja_JP': {'flag': 'jp', 'name': 'Japanese'} } - +FAB_API_MAX_PAGE_SIZE=100 #------------------------------ # GLOBALS FOR GENERAL APP's #------------------------------ diff --git a/flask_appbuilder/__init__.py b/flask_appbuilder/__init__.py index 8da6221e7..4e2619a39 100644 --- a/flask_appbuilder/__init__.py +++ b/flask_appbuilder/__init__.py @@ -4,8 +4,9 @@ from .models.sqla import Model, Base, SQLA from .base import AppBuilder from .baseviews import expose, BaseView -from .views import ModelView, IndexView, SimpleFormView, PublicFormView, MasterDetailView, MultipleView, \ - RestCRUDView, CompactCRUDMixin +from .views import ModelView, IndexView, SimpleFormView, PublicFormView, \ + MasterDetailView, MultipleView, RestCRUDView, CompactCRUDMixin +from .api import ModelRestApi from .charts.views import GroupByChartView, DirectByChartView from .models.group import aggregate_count, aggregate_avg, aggregate_sum from .actions import action diff --git a/flask_appbuilder/api/__init__.py b/flask_appbuilder/api/__init__.py new file mode 100644 index 000000000..422c12158 --- /dev/null +++ b/flask_appbuilder/api/__init__.py @@ -0,0 +1,1625 @@ +import functools +import logging +import re +import traceback + +from apispec import yaml_utils +from flask import Blueprint, current_app, jsonify, make_response, request +from flask_babel import lazy_gettext as _ +import jsonschema +from marshmallow import ValidationError +from marshmallow_sqlalchemy.fields import Related, RelatedList +import prison +from sqlalchemy.exc import IntegrityError +from werkzeug.exceptions import BadRequest +import yaml + +from .convert import Model2SchemaConverter +from .schemas import get_info_schema, get_item_schema, get_list_schema +from .._compat import as_unicode +from ..const import ( + API_ADD_COLUMNS_RES_KEY, + API_ADD_COLUMNS_RIS_KEY, + API_ADD_TITLE_RES_KEY, + API_ADD_TITLE_RIS_KEY, + API_DESCRIPTION_COLUMNS_RES_KEY, + API_DESCRIPTION_COLUMNS_RIS_KEY, + API_EDIT_COLUMNS_RES_KEY, + API_EDIT_COLUMNS_RIS_KEY, + API_EDIT_TITLE_RES_KEY, + API_EDIT_TITLE_RIS_KEY, + API_FILTERS_RES_KEY, + API_FILTERS_RIS_KEY, + API_LABEL_COLUMNS_RES_KEY, + API_LABEL_COLUMNS_RIS_KEY, + API_LIST_COLUMNS_RES_KEY, + API_LIST_COLUMNS_RIS_KEY, + API_LIST_TITLE_RES_KEY, + API_LIST_TITLE_RIS_KEY, + API_ORDER_COLUMN_RIS_KEY, + API_ORDER_COLUMNS_RES_KEY, + API_ORDER_COLUMNS_RIS_KEY, + API_ORDER_DIRECTION_RIS_KEY, + API_PAGE_INDEX_RIS_KEY, + API_PAGE_SIZE_RIS_KEY, + API_PERMISSIONS_RES_KEY, + API_PERMISSIONS_RIS_KEY, + API_RESULT_RES_KEY, + API_SELECT_COLUMNS_RIS_KEY, + API_SHOW_COLUMNS_RES_KEY, + API_SHOW_COLUMNS_RIS_KEY, + API_SHOW_TITLE_RES_KEY, + API_SHOW_TITLE_RIS_KEY, + API_URI_RIS_KEY, +) +from ..security.decorators import permission_name, protect + +log = logging.getLogger(__name__) + + +def get_error_msg(): + """ + (inspired on Superset code) + :return: (str) + """ + if current_app.config.get("FAB_API_SHOW_STACKTRACE"): + return traceback.format_exc() + return "Fatal error" + + +def safe(f): + """ + A decorator that catches uncaught exceptions and + return the response in JSON format (inspired on Superset code) + """ + + def wraps(self, *args, **kwargs): + try: + return f(self, *args, **kwargs) + except BadRequest as e: + return self.response_400(message=str(e)) + except Exception as e: + logging.exception(e) + return self.response_500(message=get_error_msg()) + + return functools.update_wrapper(wraps, f) + + +def rison(schema=None): + """ + Use this decorator to parse URI *Rison* arguments to + a python data structure, your method gets the data + structure on kwargs['rison']. Response is HTTP 400 + if *Rison* is not correct:: + + class ExampleApi(BaseApi): + @expose('/risonjson') + @rison() + def rison_json(self, **kwargs): + return self.response(200, result=kwargs['rison']) + + You can additionally pass a JSON schema to + validate Rison arguments:: + + schema = { + "type": "object", + "properties": { + "arg1": { + "type": "integer" + } + } + } + + class ExampleApi(BaseApi): + @expose('/risonjson') + @rison(schema) + def rison_json(self, **kwargs): + return self.response(200, result=kwargs['rison']) + + """ + + def _rison(f): + def wraps(self, *args, **kwargs): + value = request.args.get(API_URI_RIS_KEY, None) + kwargs["rison"] = dict() + if value: + try: + kwargs["rison"] = prison.loads(value) + except prison.decoder.ParserException: + return self.response_400(message="Not a valid rison argument") + if schema: + try: + jsonschema.validate(instance=kwargs["rison"], schema=schema) + except jsonschema.ValidationError as e: + return self.response_400( + message="Not a valid rison schema {}".format(e) + ) + return f(self, *args, **kwargs) + + return functools.update_wrapper(wraps, f) + + return _rison + + +def expose(url="/", methods=("GET",)): + """ + Use this decorator to expose API endpoints on your API classes. + + :param url: + Relative URL for the endpoint + :param methods: + Allowed HTTP methods. By default only GET is allowed. + """ + + def wrap(f): + if not hasattr(f, "_urls"): + f._urls = [] + f._urls.append((url, methods)) + return f + + return wrap + + +def merge_response_func(func, key): + """ + Use this decorator to set a new merging + response function to HTTP endpoints + + candidate function must have the following signature + and be childs of BaseApi: + ``` + def merge_some_function(self, response, rison_args): + ``` + + :param func: Name of the merge function where the key is allowed + :param key: The key name for rison selection + :return: None + """ + + def wrap(f): + if not hasattr(f, "_response_key_func_mappings"): + f._response_key_func_mappings = dict() + f._response_key_func_mappings[key] = func + return f + + return wrap + + +class BaseApi(object): + """ + All apis inherit from this class. + it's constructor will register your exposed urls on flask + as a Blueprint. + + This class does not expose any urls, + but provides a common base for all APIS. + """ + + appbuilder = None + blueprint = None + endpoint = None + + version = "v1" + """ + Define the Api version for this resource/class + """ + route_base = None + """ + Define the route base where all methods will suffix from + """ + resource_name = None + """ + Defines a custom resource name, overrides the inferred from Class name + makes no sense to use it with route base + """ + base_permissions = None + """ + A list of allowed base permissions:: + + class ExampleApi(BaseApi): + base_permissions = ['can_get'] + + """ + allow_browser_login = False + """ + Will allow flask-login cookie authorization on the API + default is False. + """ + extra_args = None + + apispec_parameter_schemas = None + """ + Set your custom Rison parameter schemas here so that + they get registered on the OpenApi spec:: + + custom_parameter = { + "type": "object" + "properties": { + "name": { + "type": "string" + } + } + } + + class CustomApi(BaseApi): + apispec_parameter_schemas = { + "custom_parameter": custom_parameter + } + """ + _apispec_parameter_schemas = None + + responses = { + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"message": {"type": "string"}}, + } + } + }, + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"message": {"type": "string"}}, + } + } + }, + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"message": {"type": "string"}}, + } + } + }, + }, + "422": { + "description": "Could not process entity", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"message": {"type": "string"}}, + } + } + }, + }, + "500": { + "description": "Fatal error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"message": {"type": "string"}}, + } + } + }, + }, + } + """ + Override custom OpenApi responses + """ + + def __init__(self): + """ + Initialization of base permissions + based on exposed methods and actions + + Initialization of extra args + """ + self._response_key_func_mappings = dict() + self.apispec_parameter_schemas = self.apispec_parameter_schemas or dict() + self._apispec_parameter_schemas = self._apispec_parameter_schemas or dict() + self._apispec_parameter_schemas.update(self.apispec_parameter_schemas) + if self.base_permissions is None: + self.base_permissions = set() + for attr_name in dir(self): + if hasattr(getattr(self, attr_name), "_permission_name"): + _permission_name = getattr( + getattr(self, attr_name), "_permission_name" + ) + self.base_permissions.add("can_" + _permission_name) + self.base_permissions = list(self.base_permissions) + if not self.extra_args: + self.extra_args = dict() + self._apis = dict() + for attr_name in dir(self): + if hasattr(getattr(self, attr_name), "_extra"): + _extra = getattr(getattr(self, attr_name), "_extra") + for key in _extra: + self._apis[key] = _extra[key] + + def create_blueprint(self, appbuilder, endpoint=None, static_folder=None): + # Store appbuilder instance + self.appbuilder = appbuilder + # If endpoint name is not provided, get it from the class name + self.endpoint = endpoint or self.__class__.__name__ + self.resource_name = self.resource_name or self.__class__.__name__ + + if self.route_base is None: + self.route_base = "/api/{}/{}".format( + self.version, self.resource_name.lower() + ) + self.blueprint = Blueprint(self.endpoint, __name__, url_prefix=self.route_base) + + self._register_urls() + return self.blueprint + + def add_api_spec(self, api_spec): + for attr_name in dir(self): + attr = getattr(self, attr_name) + if hasattr(attr, "_urls"): + for url, methods in attr._urls: + operations = dict() + path = self.path_helper(path=url, operations=operations) + self.operation_helper( + path=path, operations=operations, methods=methods, func=attr + ) + api_spec.path(path=path, operations=operations) + for operation in operations: + api_spec._paths[path][operation]["tags"] = [ + self.__class__.__name__ + ] + self.add_apispec_components(api_spec) + + def add_apispec_components(self, api_spec): + for k, v in self.responses.items(): + api_spec.components._responses[k] = v + for k, v in self._apispec_parameter_schemas.items(): + if k not in api_spec.components._parameters: + _v = { + "in": "query", + "name": API_URI_RIS_KEY, + "schema": {"$ref": "#/components/schemas/{}".format(k)}, + } + # Using private because parameter method does not behave correctly + api_spec.components._schemas[k] = v + api_spec.components._parameters[k] = _v + + def _register_urls(self): + for attr_name in dir(self): + attr = getattr(self, attr_name) + if hasattr(attr, "_urls"): + for url, methods in attr._urls: + self.blueprint.add_url_rule(url, attr_name, attr, methods=methods) + + def path_helper(self, path=None, operations=None, **kwargs): + """ + Works like a apispec plugin + May return a path as string and mutate operations dict. + + :param str path: Path to the resource + :param dict operations: A `dict` mapping HTTP methods to operation object. See + https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject + :param kwargs: + :return: Return value should be a string or None. If a string is returned, it + is set as the path. + """ + RE_URL = re.compile(r"<(?:[^:<>]+:)?([^<>]+)>") + path = RE_URL.sub(r"{\1}", path) + return "/{}{}".format(self.resource_name, path) + + def operation_helper( + self, path=None, operations=None, methods=None, func=None, **kwargs + ): + """May mutate operations. + :param str path: Path to the resource + :param dict operations: A `dict` mapping HTTP methods to operation object. See + :param list methods: A list of methods registered for this path + """ + for method in methods: + yaml_doc_string = yaml_utils.load_operations_from_docstring(func.__doc__) + yaml_doc_string = yaml.safe_load( + str(yaml_doc_string).replace( + "{{self.__class__.__name__}}", self.__class__.__name__ + ) + ) + if yaml_doc_string: + operations[method.lower()] = yaml_doc_string.get(method.lower(), {}) + else: + operations[method.lower()] = {} + + @staticmethod + def _prettify_name(name): + """ + Prettify pythonic variable name. + + For example, 'HelloWorld' will be converted to 'Hello World' + + :param name: + Name to prettify. + """ + return re.sub(r"(?<=.)([A-Z])", r" \1", name) + + @staticmethod + def _prettify_column(name): + """ + Prettify pythonic variable name. + + For example, 'hello_world' will be converted to 'Hello World' + + :param name: + Name to prettify. + """ + return re.sub("[._]", " ", name).title() + + def get_uninit_inner_views(self): + """ + Will return a list with views that need to be initialized. + Normally related_views from ModelView + """ + return [] + + def get_init_inner_views(self, views): + """ + Sets initialized inner views + """ + pass + + def set_response_key_mappings(self, response, func, rison_args, **kwargs): + if not hasattr(func, "_response_key_func_mappings"): + return + _keys = rison_args.get("keys", None) + if not _keys: + for k, v in func._response_key_func_mappings.items(): + v(self, response, **kwargs) + else: + for k, v in func._response_key_func_mappings.items(): + if k in _keys: + v(self, response, **kwargs) + + def merge_current_user_permissions(self, response, **kwargs): + response[ + API_PERMISSIONS_RES_KEY + ] = self.appbuilder.sm.get_user_permissions_on_view(self.__class__.__name__) + + @staticmethod + def response(code, **kwargs): + """ + Generic HTTP JSON response method + + :param code: HTTP code (int) + :param kwargs: Data structure for response (dict) + :return: HTTP Json response + """ + _ret_json = jsonify(kwargs) + resp = make_response(_ret_json, code) + resp.headers["Content-Type"] = "application/json; charset=utf-8" + return resp + + def response_400(self, message=None): + """ + Helper method for HTTP 400 response + + :param message: Error message (str) + :return: HTTP Json response + """ + message = message or "Arguments are not correct" + return self.response(400, **{"message": message}) + + def response_422(self, message=None): + """ + Helper method for HTTP 422 response + + :param message: Error message (str) + :return: HTTP Json response + """ + message = message or "Could not process entity" + return self.response(422, **{"message": message}) + + def response_401(self): + """ + Helper method for HTTP 401 response + + :param message: Error message (str) + :return: HTTP Json response + """ + return self.response(401, **{"message": "Not authorized"}) + + def response_404(self): + """ + Helper method for HTTP 404 response + + :param message: Error message (str) + :return: HTTP Json response + """ + return self.response(404, **{"message": "Not found"}) + + def response_500(self, message=None): + """ + Helper method for HTTP 500 response + + :param message: Error message (str) + :return: HTTP Json response + """ + message = message or "Internal error" + return self.response(500, **{"message": message}) + + +class BaseModelApi(BaseApi): + datamodel = None + """ + Your sqla model you must initialize it like:: + + class MyModelApi(BaseModelApi): + datamodel = SQLAInterface(MyTable) + """ + search_columns = None + """ + List with allowed search columns, if not provided all possible search + columns will be used. If you want to limit the search (*filter*) columns + possibilities, define it with a list of column names from your model:: + + class MyView(ModelRestApi): + datamodel = SQLAInterface(MyTable) + search_columns = ['name', 'address'] + + """ + search_exclude_columns = None + """ + List with columns to exclude from search. Search includes all possible + columns by default + """ + label_columns = None + """ + Dictionary of labels for your columns, override this if you want + different pretify labels + + example (will just override the label for name column):: + + class MyView(ModelRestApi): + datamodel = SQLAInterface(MyTable) + label_columns = {'name':'My Name Label Override'} + + """ + base_filters = None + """ + Filter the view use: [['column_name',BaseFilter,'value'],] + + example:: + + def get_user(): + return g.user + + class MyView(ModelRestApi): + datamodel = SQLAInterface(MyTable) + base_filters = [['created_by', FilterEqualFunction, get_user], + ['name', FilterStartsWith, 'a']] + + """ + + base_order = None + """ + Use this property to set default ordering for lists + ('col_name','asc|desc'):: + + class MyView(ModelRestApi): + datamodel = SQLAInterface(MyTable) + base_order = ('my_column_name','asc') + + """ + _base_filters = None + """ Internal base Filter from class Filters will always filter view """ + _filters = None + """ + Filters object will calculate all possible filter types + based on search_columns + """ + + def __init__(self, **kwargs): + """ + Constructor + """ + datamodel = kwargs.get("datamodel", None) + if datamodel: + self.datamodel = datamodel + self._init_properties() + self._init_titles() + super(BaseModelApi, self).__init__() + + def _gen_labels_columns(self, list_columns): + """ + Auto generates pretty label_columns from list of columns + """ + for col in list_columns: + if not self.label_columns.get(col): + self.label_columns[col] = self._prettify_column(col) + + def _label_columns_json(self, cols=None): + """ + Prepares dict with labels to be JSON serializable + """ + ret = {} + cols = cols or [] + d = {k: v for (k, v) in self.label_columns.items() if k in cols} + for key, value in d.items(): + ret[key] = as_unicode(_(value).encode("UTF-8")) + return ret + + def _init_properties(self): + self.label_columns = self.label_columns or {} + self.base_filters = self.base_filters or [] + self.search_exclude_columns = self.search_exclude_columns or [] + self.search_columns = self.search_columns or [] + + self._base_filters = self.datamodel.get_filters().add_filter_list( + self.base_filters + ) + list_cols = self.datamodel.get_columns_list() + search_columns = self.datamodel.get_search_columns_list() + if not self.search_columns: + self.search_columns = [ + x for x in search_columns if x not in self.search_exclude_columns + ] + + self._gen_labels_columns(list_cols) + self._filters = self.datamodel.get_filters(self.search_columns) + + def _init_titles(self): + pass + + +class ModelRestApi(BaseModelApi): + list_title = "" + """ + List Title, if not configured the default is + 'List ' with pretty model name + """ + show_title = "" + """ + Show Title , if not configured the default is + 'Show ' with pretty model name + """ + add_title = "" + """ + Add Title , if not configured the default is + 'Add ' with pretty model name + """ + edit_title = "" + """ + Edit Title , if not configured the default is + 'Edit ' with pretty model name + """ + + list_columns = None + """ + A list of columns (or model's methods) to be displayed on the list view. + Use it to control the order of the display + """ + show_columns = None + """ + A list of columns (or model's methods) for the get item endpoint. + Use it to control the order of the results + """ + add_columns = None + """ + A list of columns (or model's methods) to be allowed to post + """ + edit_columns = None + """ + A list of columns (or model's methods) to be allowed to update + """ + list_exclude_columns = None + """ + A list of columns to exclude from the get list endpoint. + By default all columns are included. + """ + show_exclude_columns = None + """ + A list of columns to exclude from the get item endpoint. + By default all columns are included. + """ + add_exclude_columns = None + """ + A list of columns to exclude from the add endpoint. + By default all columns are included. + """ + edit_exclude_columns = None + """ + A list of columns to exclude from the edit endpoint. + By default all columns are included. + """ + order_columns = None + """ Allowed order columns """ + page_size = 20 + """ + Use this property to change default page size + """ + description_columns = None + """ + Dictionary with column descriptions that will be shown on the forms:: + + class MyView(ModelView): + datamodel = SQLAModel(MyTable, db.session) + + description_columns = {'name':'your models name column', + 'address':'the address column'} + """ + validators_columns = None + """ Dictionary to add your own validators for forms """ + + add_query_rel_fields = None + """ + Add Customized query for related add fields. + Assign a dictionary where the keys are the column names of + the related models to filter, the value for each key, is a list of lists with the + same format as base_filter + {'relation col name':[['Related model col',FilterClass,'Filter Value'],...],...} + Add a custom filter to form related fields:: + + class ContactModelView(ModelRestApi): + datamodel = SQLAModel(Contact) + add_query_rel_fields = {'group':[['name',FilterStartsWith,'W']]} + + """ + edit_query_rel_fields = None + """ + Add Customized query for related edit fields. + Assign a dictionary where the keys are the column names of + the related models to filter, the value for each key, is a list of lists with the + same format as base_filter + {'relation col name':[['Related model col',FilterClass,'Filter Value'],...],...} + Add a custom filter to form related fields:: + + class ContactModelView(ModelRestApi): + datamodel = SQLAModel(Contact, db.session) + edit_query_rel_fields = {'group':[['name',FilterStartsWith,'W']]} + + """ + order_rel_fields = None + """ + Impose order on related fields. + assign a dictionary where the keys are the related column names:: + + class ContactModelView(ModelRestApi): + datamodel = SQLAModel(Contact) + order_rel_fields = { + 'group': ('name', 'asc') + 'gender': ('name', 'asc') + } + """ + list_model_schema = None + """ + Override to provide your own marshmallow Schema + for JSON to SQLA dumps + """ + add_model_schema = None + """ + Override to provide your own marshmallow Schema + for JSON to SQLA dumps + """ + edit_model_schema = None + """ + Override to provide your own marshmallow Schema + for JSON to SQLA dumps + """ + show_model_schema = None + """ + Override to provide your own marshmallow Schema + for JSON to SQLA dumps + """ + model2schemaconverter = Model2SchemaConverter + """ + Override to use your own Model2SchemaConverter + (inherit from BaseModel2SchemaConverter) + """ + _apispec_parameter_schemas = { + "get_info_schema": get_info_schema, + "get_item_schema": get_item_schema, + "get_list_schema": get_list_schema, + } + + def __init__(self): + super(ModelRestApi, self).__init__() + self.validators_columns = self.validators_columns or {} + self.model2schemaconverter = self.model2schemaconverter( + self.datamodel, self.validators_columns + ) + + def create_blueprint(self, appbuilder, *args, **kwargs): + self._init_model_schemas() + return super(ModelRestApi, self).create_blueprint(appbuilder, *args, **kwargs) + + def add_apispec_components(self, api_spec): + super(ModelRestApi, self).add_apispec_components(api_spec) + api_spec.components.schema( + "{}.{}".format(self.__class__.__name__, "get_list"), + schema=self.list_model_schema, + ) + api_spec.components.schema( + "{}.{}".format(self.__class__.__name__, "post"), + schema=self.add_model_schema, + ) + api_spec.components.schema( + "{}.{}".format(self.__class__.__name__, "put"), + schema=self.edit_model_schema, + ) + api_spec.components.schema( + "{}.{}".format(self.__class__.__name__, "get"), + schema=self.show_model_schema, + ) + + def _init_model_schemas(self): + # Create Marshmalow schemas if one is not specified + if self.list_model_schema is None: + self.list_model_schema = self.model2schemaconverter.convert( + self.list_columns + ) + if self.add_model_schema is None: + self.add_model_schema = self.model2schemaconverter.convert( + self.add_columns, nested=False, enum_dump_by_name=True + ) + if self.edit_model_schema is None: + self.edit_model_schema = self.model2schemaconverter.convert( + self.edit_columns, nested=False, enum_dump_by_name=True + ) + if self.show_model_schema is None: + self.show_model_schema = self.model2schemaconverter.convert( + self.show_columns + ) + + def _init_titles(self): + """ + Init Titles if not defined + """ + super(ModelRestApi, self)._init_titles() + class_name = self.datamodel.model_name + if not self.list_title: + self.list_title = "List " + self._prettify_name(class_name) + if not self.add_title: + self.add_title = "Add " + self._prettify_name(class_name) + if not self.edit_title: + self.edit_title = "Edit " + self._prettify_name(class_name) + if not self.show_title: + self.show_title = "Show " + self._prettify_name(class_name) + self.title = self.list_title + + def _init_properties(self): + """ + Init Properties + """ + super(ModelRestApi, self)._init_properties() + # Reset init props + self.description_columns = self.description_columns or {} + self.list_exclude_columns = self.list_exclude_columns or [] + self.show_exclude_columns = self.show_exclude_columns or [] + self.add_exclude_columns = self.add_exclude_columns or [] + self.edit_exclude_columns = self.edit_exclude_columns or [] + self.order_rel_fields = self.order_rel_fields or {} + # Generate base props + list_cols = self.datamodel.get_user_columns_list() + if not self.list_columns and self.list_model_schema: + list(self.list_model_schema._declared_fields.keys()) + else: + self.list_columns = self.list_columns or [ + x + for x in self.datamodel.get_user_columns_list() + if x not in self.list_exclude_columns + ] + + self._gen_labels_columns(self.list_columns) + self.order_columns = ( + self.order_columns or + self.datamodel.get_order_columns_list(list_columns=self.list_columns) + ) + # Process excluded columns + if not self.show_columns: + self.show_columns = [ + x for x in list_cols if x not in self.show_exclude_columns + ] + if not self.add_columns: + self.add_columns = [ + x for x in list_cols if x not in self.add_exclude_columns + ] + if not self.edit_columns: + self.edit_columns = [ + x for x in list_cols if x not in self.edit_exclude_columns + ] + self._filters = self.datamodel.get_filters(self.search_columns) + self.edit_query_rel_fields = self.edit_query_rel_fields or dict() + self.add_query_rel_fields = self.add_query_rel_fields or dict() + + def merge_add_field_info(self, response, **kwargs): + _kwargs = kwargs.get("add_columns", {}) + response[API_ADD_COLUMNS_RES_KEY] = self._get_fields_info( + self.add_columns, + self.add_model_schema, + self.add_query_rel_fields, + **_kwargs + ) + + def merge_edit_field_info(self, response, **kwargs): + _kwargs = kwargs.get("edit_columns", {}) + response[API_EDIT_COLUMNS_RES_KEY] = self._get_fields_info( + self.edit_columns, + self.edit_model_schema, + self.edit_query_rel_fields, + **_kwargs + ) + + def merge_search_filters(self, response, **kwargs): + # Get possible search fields and all possible operations + search_filters = dict() + dict_filters = self._filters.get_search_filters() + for col in self.search_columns: + search_filters[col] = [ + {"name": as_unicode(flt.name), "operator": flt.arg_name} + for flt in dict_filters[col] + ] + response[API_FILTERS_RES_KEY] = search_filters + + def merge_add_title(self, response, **kwargs): + response[API_ADD_TITLE_RES_KEY] = self.add_title + + def merge_edit_title(self, response, **kwargs): + response[API_EDIT_TITLE_RES_KEY] = self.edit_title + + def merge_label_columns(self, response, **kwargs): + _pruned_select_cols = kwargs.get(API_SELECT_COLUMNS_RIS_KEY, []) + if _pruned_select_cols: + _show_columns = _pruned_select_cols + else: + _show_columns = self.show_columns + response[API_LABEL_COLUMNS_RES_KEY] = self._label_columns_json(_show_columns) + + def merge_show_columns(self, response, **kwargs): + _pruned_select_cols = kwargs.get(API_SELECT_COLUMNS_RIS_KEY, []) + if _pruned_select_cols: + response[API_SHOW_COLUMNS_RES_KEY] = _pruned_select_cols + else: + response[API_SHOW_COLUMNS_RES_KEY] = self.show_columns + + def merge_description_columns(self, response, **kwargs): + _pruned_select_cols = kwargs.get(API_SELECT_COLUMNS_RIS_KEY, []) + if _pruned_select_cols: + response[API_DESCRIPTION_COLUMNS_RES_KEY] = self._description_columns_json( + _pruned_select_cols + ) + else: + response[API_DESCRIPTION_COLUMNS_RES_KEY] = self._description_columns_json( + self.show_columns + ) + + def merge_list_columns(self, response, **kwargs): + _pruned_select_cols = kwargs.get(API_SELECT_COLUMNS_RIS_KEY, []) + if _pruned_select_cols: + response[API_LIST_COLUMNS_RES_KEY] = _pruned_select_cols + else: + response[API_LIST_COLUMNS_RES_KEY] = self.list_columns + + def merge_order_columns(self, response, **kwargs): + _pruned_select_cols = kwargs.get(API_SELECT_COLUMNS_RIS_KEY, []) + if _pruned_select_cols: + response[API_ORDER_COLUMNS_RES_KEY] = [ + order_col + for order_col in self.order_columns + if order_col in _pruned_select_cols + ] + else: + response[API_ORDER_COLUMNS_RES_KEY] = self.order_columns + + def merge_list_title(self, response, **kwargs): + response[API_LIST_TITLE_RES_KEY] = self.list_title + + def merge_show_title(self, response, **kwargs): + response[API_SHOW_TITLE_RES_KEY] = self.show_title + + @expose("/_info", methods=["GET"]) + @protect() + @safe + @rison(get_info_schema) + @permission_name("info") + @merge_response_func( + BaseApi.merge_current_user_permissions, API_PERMISSIONS_RIS_KEY + ) + @merge_response_func(merge_add_field_info, API_ADD_COLUMNS_RIS_KEY) + @merge_response_func(merge_edit_field_info, API_EDIT_COLUMNS_RIS_KEY) + @merge_response_func(merge_search_filters, API_FILTERS_RIS_KEY) + @merge_response_func(merge_add_title, API_ADD_TITLE_RIS_KEY) + @merge_response_func(merge_edit_title, API_EDIT_TITLE_RIS_KEY) + def info(self, **kwargs): + """ Endpoint that renders a response for CRUD REST meta data + --- + get: + parameters: + - $ref: '#/components/parameters/get_info_schema' + responses: + 200: + description: Item from Model + content: + application/json: + schema: + type: object + properties: + add_columns: + type: object + edit_columns: + type: object + filters: + type: object + permissions: + type: array + items: + type: string + 400: + $ref: '#/components/responses/400' + 401: + $ref: '#/components/responses/401' + 422: + $ref: '#/components/responses/422' + 500: + $ref: '#/components/responses/500' + """ + _response = dict() + _args = kwargs.get("rison", {}) + self.set_response_key_mappings(_response, self.info, _args, **_args) + return self.response(200, **_response) + + @expose("/", methods=["GET"]) + @protect() + @safe + @permission_name("get") + @rison(get_item_schema) + @merge_response_func(merge_label_columns, API_LABEL_COLUMNS_RIS_KEY) + @merge_response_func(merge_show_columns, API_SHOW_COLUMNS_RIS_KEY) + @merge_response_func(merge_description_columns, API_DESCRIPTION_COLUMNS_RIS_KEY) + @merge_response_func(merge_show_title, API_SHOW_TITLE_RIS_KEY) + def get(self, pk, **kwargs): + """Get item from Model + --- + get: + parameters: + - in: path + schema: + type: integer + name: pk + - $ref: '#/components/parameters/get_item_schema' + responses: + 200: + description: Item from Model + content: + application/json: + schema: + type: object + properties: + label_columns: + type: object + show_columns: + type: array + items: + type: string + description_columns: + type: object + show_title: + type: string + id: + type: string + result: + $ref: '#/components/schemas/{{self.__class__.__name__}}.get' + 400: + $ref: '#/components/responses/400' + 401: + $ref: '#/components/responses/401' + 404: + $ref: '#/components/responses/404' + 422: + $ref: '#/components/responses/422' + 500: + $ref: '#/components/responses/500' + """ + item = self.datamodel.get(pk, self._base_filters) + if not item: + return self.response_404() + + _response = dict() + _args = kwargs.get("rison", {}) + select_cols = _args.get(API_SELECT_COLUMNS_RIS_KEY, []) + _pruned_select_cols = [col for col in select_cols if col in self.show_columns] + self.set_response_key_mappings( + _response, + self.get, + _args, + **{API_SELECT_COLUMNS_RIS_KEY: _pruned_select_cols} + ) + if _pruned_select_cols: + _show_model_schema = self.model2schemaconverter.convert(_pruned_select_cols) + else: + _show_model_schema = self.show_model_schema + + _response["id"] = pk + _response[API_RESULT_RES_KEY] = _show_model_schema.dump(item, many=False).data + self.pre_get(_response) + return self.response(200, **_response) + + @expose("/", methods=["GET"]) + @protect() + @safe + @permission_name("get") + @rison(get_list_schema) + @merge_response_func(merge_order_columns, API_ORDER_COLUMNS_RIS_KEY) + @merge_response_func(merge_label_columns, API_LABEL_COLUMNS_RIS_KEY) + @merge_response_func(merge_description_columns, API_DESCRIPTION_COLUMNS_RIS_KEY) + @merge_response_func(merge_list_columns, API_LIST_COLUMNS_RIS_KEY) + @merge_response_func(merge_list_title, API_LIST_TITLE_RIS_KEY) + def get_list(self, **kwargs): + """Get list of items from Model + --- + get: + parameters: + - $ref: '#/components/parameters/get_list_schema' + responses: + 200: + description: Items from Model + content: + application/json: + schema: + type: object + properties: + label_columns: + type: object + list_columns: + type: array + items: + type: string + description_columns: + type: object + list_title: + type: string + ids: + type: array + items: + type: string + order_columns: + type: array + items: + type: string + result: + type: array + items: + $ref: '#/components/schemas/{{self.__class__.__name__}}.get_list' # noqa + 400: + $ref: '#/components/responses/400' + 401: + $ref: '#/components/responses/401' + 422: + $ref: '#/components/responses/422' + 500: + $ref: '#/components/responses/500' + """ + _response = dict() + _args = kwargs.get("rison", {}) + # handle select columns + select_cols = _args.get(API_SELECT_COLUMNS_RIS_KEY, []) + _pruned_select_cols = [col for col in select_cols if col in self.list_columns] + self.set_response_key_mappings( + _response, + self.get_list, + _args, + **{API_SELECT_COLUMNS_RIS_KEY: _pruned_select_cols} + ) + + if _pruned_select_cols: + _list_model_schema = self.model2schemaconverter.convert(_pruned_select_cols) + else: + _list_model_schema = self.list_model_schema + # handle filters + joined_filters = self._handle_filters_args(_args) + # handle base order + order_column, order_direction = self._handle_order_args(_args) + # handle pagination + page_index, page_size = self._handle_page_args(_args) + # Make the query + query_select_columns = _pruned_select_cols or self.list_columns + count, lst = self.datamodel.query( + joined_filters, + order_column, + order_direction, + page=page_index, + page_size=page_size, + select_columns=query_select_columns, + ) + pks = self.datamodel.get_keys(lst) + _response[API_RESULT_RES_KEY] = _list_model_schema.dump(lst, many=True).data + _response["ids"] = pks + _response["count"] = count + self.pre_get_list(_response) + return self.response(200, **_response) + + @expose("/", methods=["POST"]) + @protect() + @safe + @permission_name("post") + def post(self): + """POST item to Model + --- + post: + requestBody: + description: Model schema + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/{{self.__class__.__name__}}.post' + responses: + 201: + description: Item inserted + content: + application/json: + schema: + type: object + properties: + id: + type: string + result: + $ref: '#/components/schemas/{{self.__class__.__name__}}.post' + 400: + $ref: '#/components/responses/400' + 401: + $ref: '#/components/responses/401' + 422: + $ref: '#/components/responses/422' + 500: + $ref: '#/components/responses/500' + """ + if not request.is_json: + return self.response_400(message="Request is not JSON") + try: + item = self.add_model_schema.load(request.json) + except ValidationError as err: + return self.response_422(message=err.messages) + # This validates custom Schema with custom validations + if isinstance(item.data, dict): + return self.response_422(message=item.errors) + self.pre_add(item.data) + try: + self.datamodel.add(item.data, raise_exception=True) + self.post_add(item.data) + return self.response( + 201, + **{ + API_RESULT_RES_KEY: self.add_model_schema.dump( + item.data, many=False + ).data, + "id": self.datamodel.get_pk_value(item.data), + } + ) + except IntegrityError as e: + return self.response_422(message=str(e.orig)) + + @expose("/", methods=["PUT"]) + @protect() + @safe + @permission_name("put") + def put(self, pk): + """POST item to Model + --- + put: + parameters: + - in: path + schema: + type: integer + name: pk + requestBody: + description: Model schema + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/{{self.__class__.__name__}}.put' + responses: + 200: + description: Item changed + content: + application/json: + schema: + type: object + properties: + result: + $ref: '#/components/schemas/{{self.__class__.__name__}}.put' + 400: + $ref: '#/components/responses/400' + 401: + $ref: '#/components/responses/401' + 404: + $ref: '#/components/responses/404' + 422: + $ref: '#/components/responses/422' + 500: + $ref: '#/components/responses/500' + """ + item = self.datamodel.get(pk, self._base_filters) + if not request.is_json: + return self.response(400, **{"message": "Request is not JSON"}) + if not item: + return self.response_404() + try: + data = self._merge_update_item(item, request.json) + item = self.edit_model_schema.load(data, instance=item) + except ValidationError as err: + return self.response_422(message=err.messages) + # This validates custom Schema with custom validations + if isinstance(item.data, dict): + return self.response_422(message=item.errors) + self.pre_update(item.data) + try: + self.datamodel.edit(item.data, raise_exception=True) + self.post_update(item) + return self.response( + 200, + **{ + API_RESULT_RES_KEY: self.edit_model_schema.dump( + item.data, many=False + ).data + } + ) + except IntegrityError as e: + return self.response_422(message=str(e.orig)) + + @expose("/", methods=["DELETE"]) + @protect() + @safe + @permission_name("delete") + def delete(self, pk): + """Delete item from Model + --- + delete: + parameters: + - in: path + schema: + type: integer + name: pk + responses: + 200: + description: Item deleted + content: + application/json: + schema: + type: object + properties: + message: + type: string + 404: + $ref: '#/components/responses/404' + 422: + $ref: '#/components/responses/422' + 500: + $ref: '#/components/responses/500' + """ + item = self.datamodel.get(pk, self._base_filters) + if not item: + return self.response_404() + self.pre_delete(item) + try: + self.datamodel.delete(item, raise_exception=True) + self.post_delete(item) + return self.response(200, message="OK") + except IntegrityError as e: + return self.response_422(message=str(e.orig)) + + """ + ------------------------------------------------ + HELPER FUNCTIONS + ------------------------------------------------ + """ + + def _handle_page_args(self, rison_args): + """ + Helper function to handle rison page + arguments, sets defaults and impose + FAB_API_MAX_PAGE_SIZE + + :param rison_args: + :return: (tuple) page, page_size + """ + page = rison_args.get(API_PAGE_INDEX_RIS_KEY, 0) + page_size = rison_args.get(API_PAGE_SIZE_RIS_KEY, self.page_size) + return self._sanitize_page_args(page, page_size) + + def _sanitize_page_args(self, page, page_size): + _page = page or 0 + _page_size = page_size or self.page_size + max_page_size = current_app.config.get("FAB_API_MAX_PAGE_SIZE") + if _page_size > max_page_size or _page_size < 1: + _page_size = max_page_size + return _page, _page_size + + def _handle_order_args(self, rison_args): + """ + Help function to handle rison order + arguments + + :param rison_args: + :return: + """ + order_column = rison_args.get(API_ORDER_COLUMN_RIS_KEY, "") + order_direction = rison_args.get(API_ORDER_DIRECTION_RIS_KEY, "") + if not order_column and self.base_order: + order_column, order_direction = self.base_order + if order_column not in self.order_columns: + return "", "" + return order_column, order_direction + + def _handle_filters_args(self, rison_args): + self._filters.clear_filters() + self._filters.rest_add_filters(rison_args.get(API_FILTERS_RIS_KEY, [])) + return self._filters.get_joined_filters(self._base_filters) + + def _description_columns_json(self, cols=None): + """ + Prepares dict with col descriptions to be JSON serializable + """ + ret = {} + cols = cols or [] + d = {k: v for (k, v) in self.description_columns.items() if k in cols} + for key, value in d.items(): + ret[key] = as_unicode(_(value).encode("UTF-8")) + return ret + + def _get_field_info(self, field, filter_rel_field, page=None, page_size=None): + """ + Return a dict with field details + ready to serve as a response + + :param field: marshmallow field + :return: dict with field details + """ + ret = dict() + ret["name"] = field.name + ret["label"] = self.label_columns.get(field.name, "") + ret["description"] = self.description_columns.get(field.name, "") + # Handles related fields + if isinstance(field, Related) or isinstance(field, RelatedList): + ret["count"], ret["values"] = self._get_list_related_field( + field, filter_rel_field, page=page, page_size=page_size + ) + if field.validate and isinstance(field.validate, list): + ret["validate"] = [str(v) for v in field.validate] + elif field.validate: + ret["validate"] = [str(field.validate)] + ret["type"] = field.__class__.__name__ + ret["required"] = field.required + ret["unique"] = field.unique + return ret + + def _get_fields_info(self, cols, model_schema, filter_rel_fields, **kwargs): + """ + Returns a dict with fields detail + from a marshmallow schema + + :param cols: list of columns to show info for + :param model_schema: Marshmallow model schema + :param filter_rel_fields: expects add_query_rel_fields or + edit_query_rel_fields + :param kwargs: Receives all rison arguments for pagination + :return: dict with all fields details + """ + ret = list() + for col in cols: + page = page_size = None + col_args = kwargs.get(col, {}) + if col_args: + page = col_args.get(API_PAGE_INDEX_RIS_KEY, None) + page_size = col_args.get(API_PAGE_SIZE_RIS_KEY, None) + ret.append( + self._get_field_info( + model_schema.fields[col], + filter_rel_fields.get(col, []), + page=page, + page_size=page_size, + ) + ) + return ret + + def _get_list_related_field( + self, field, filter_rel_field, page=None, page_size=None + ): + """ + Return a list of values for a related field + + :param field: Marshmallow field + :param filter_rel_field: Filters for the related field + :param page: The page index + :param page_size: The page size + :return: (int, list) total record count and list of dict with id and value + """ + ret = list() + if isinstance(field, Related) or isinstance(field, RelatedList): + datamodel = self.datamodel.get_related_interface(field.name) + filters = datamodel.get_filters(datamodel.get_search_columns_list()) + page, page_size = self._sanitize_page_args(page, page_size) + order_field = self.order_rel_fields.get(field.name) + if order_field: + order_column, order_direction = order_field + else: + order_column, order_direction = "", "" + if filter_rel_field: + filters = filters.add_filter_list(filter_rel_field) + count, values = datamodel.query( + filters, order_column, order_direction, page=page, page_size=page_size + ) + for value in values: + ret.append({"id": datamodel.get_pk_value(value), "value": str(value)}) + return count, ret + + def _merge_update_item(self, model_item, data): + """ + Merge a model with a python data structure + This is useful to turn PUT method into a PATCH also + :param model_item: SQLA Model + :param data: python data structure + :return: python data structure + """ + data_item = self.edit_model_schema.dump(model_item, many=False).data + for _col in self.edit_columns: + if _col not in data.keys(): + data[_col] = data_item[_col] + return data + + """ + ------------------------------------------------ + PRE AND POST METHODS + ------------------------------------------------ + """ + + def pre_update(self, item): + """ + Override this, this method is called before the update takes place. + """ + pass + + def post_update(self, item): + """ + Override this, will be called after update + """ + pass + + def pre_add(self, item): + """ + Override this, will be called before add. + """ + pass + + def post_add(self, item): + """ + Override this, will be called after update + """ + pass + + def pre_delete(self, item): + """ + Override this, will be called before delete + """ + pass + + def post_delete(self, item): + """ + Override this, will be called after delete + """ + pass + + def pre_get(self, data): + """ + Override this, will be called before data is sent + to the requester on get item endpoint. + You can use it to mutate the response sent. + Note that any new field added will not be reflected on the OpenApi spec. + """ + pass + + def pre_get_list(self, data): + """ + Override this, will be called before data is sent + to the requester on get list endpoint. + You can use it to mutate the response sent + Note that any new field added will not be reflected on the OpenApi spec. + """ + pass diff --git a/flask_appbuilder/api/convert.py b/flask_appbuilder/api/convert.py new file mode 100644 index 000000000..2910c6d53 --- /dev/null +++ b/flask_appbuilder/api/convert.py @@ -0,0 +1,215 @@ +from marshmallow import fields +from marshmallow_enum import EnumField +from marshmallow_sqlalchemy import field_for +from marshmallow_sqlalchemy.schema import ModelSchema + + +class TreeNode: + def __init__(self, data): + self.data = data + self.childs = list() + + def __repr__(self): + return "{}.{}".format(self.data, str(self.childs)) + + +class Tree: + """ + Simplistic one level Tree + """ + def __init__(self): + self.root = TreeNode('+') + + def add(self, data): + node = TreeNode(data) + self.root.childs.append(node) + + def add_child(self, parent, data): + node = TreeNode(data) + for n in self.root.childs: + if n.data == parent: + n.childs.append(node) + return + root = TreeNode(parent) + self.root.childs.append(root) + root.childs.append(node) + + def __repr__(self): + ret = "" + for node in self.root.childs: + ret += str(node) + return ret + + +def columns2Tree(columns): + tree = Tree() + for column in columns: + if '.' in column: + tree.add_child( + column.split('.')[0], + column.split('.')[1] + ) + else: + tree.add(column) + return tree + + +class BaseModel2SchemaConverter(object): + + def __init__(self, datamodel, validators_columns): + """ + :param datamodel: SQLAInterface + """ + self.datamodel = datamodel + self.validators_columns = validators_columns + + def convert(self, columns, **kwargs): + pass + + +class Model2SchemaConverter(BaseModel2SchemaConverter): + """ + Class that converts Models to marshmallow Schemas + """ + + def __init__(self, datamodel, validators_columns): + """ + :param datamodel: SQLAInterface + """ + super(Model2SchemaConverter, self).__init__(datamodel, validators_columns) + + @staticmethod + def _debug_schema(schema): + for k, v in schema._declared_fields.items(): + print(k, v) + + def _meta_schema_factory(self, columns, model, class_mixin): + """ + Creates ModelSchema marshmallow-sqlalchemy + + :param columns: a list of columns to mix + :param model: Model + :param class_mixin: a marshamallow Schema to mix + :return: ModelSchema + """ + _model = model + if columns: + class MetaSchema(ModelSchema, class_mixin): + class Meta: + model = _model + fields = columns + strict = True + sqla_session = self.datamodel.session + else: + class MetaSchema(ModelSchema, class_mixin): + class Meta: + model = _model + strict = True + sqla_session = self.datamodel.session + return MetaSchema + + def _column2field(self, datamodel, column, nested=True, enum_dump_by_name=False): + """ + + :param datamodel: SQLAInterface + :param column: TreeNode column (childs are dotted column) + :param nested: Boolean if will create nested fields + :param enum_dump_by_name: + :return: Schema.field + """ + _model = datamodel.obj + # Handle relations + if datamodel.is_relation(column.data) and nested: + required = not datamodel.is_nullable(column.data) + nested_model = datamodel.get_related_model(column.data) + lst = [item.data for item in column.childs] + nested_schema = self.convert( + lst, + nested_model, + nested=False + ) + if datamodel.is_relation_many_to_one(column.data): + many = False + elif datamodel.is_relation_many_to_many(column.data): + many = True + else: + many = False + field = fields.Nested(nested_schema, many=many, required=required) + field.unique = datamodel.is_unique(column.data) + return field + # Handle bug on marshmallow-sqlalchemy #163 + elif datamodel.is_relation(column.data): + required = not datamodel.is_nullable(column.data) + field = field_for(_model, column.data) + field.required = required + field.unique = datamodel.is_unique(column.data) + return field + # Handle Enums + elif datamodel.is_enum(column.data): + required = not datamodel.is_nullable(column.data) + enum_class = datamodel.list_columns[column.data].info.get( + 'enum_class', + datamodel.list_columns[column.data].type + ) + if enum_dump_by_name: + enum_dump_by = EnumField.NAME + else: + enum_dump_by = EnumField.VALUE + field = EnumField(enum_class, dump_by=enum_dump_by, required=required) + field.unique = datamodel.is_unique(column.data) + return field + if not hasattr(getattr(_model, column.data), '__call__'): + field = field_for(_model, column.data) + field.unique = datamodel.is_unique(column.data) + if column.data in self.validators_columns: + field.validate.append(self.validators_columns[column.data]) + return field + + @staticmethod + def get_column_child_model(column): + if '.' in column: + return column.split('.')[0] + return column + + @staticmethod + def is_column_dotted(column): + return '.' in column + + def convert(self, columns, model=None, nested=True, enum_dump_by_name=False): + """ + Creates a Marshmallow ModelSchema class + + + :param columns: List with columns to include, if empty converts all on model + :param model: Override Model to convert + :param nested: Generate relation with nested schemas + :return: ModelSchema object + """ + super(Model2SchemaConverter, self).convert( + columns, + model=model, + nested=nested + ) + + class SchemaMixin: + pass + + _model = model or self.datamodel.obj + _datamodel = self.datamodel.__class__(_model) + + ma_sqla_fields_override = {} + + _columns = list() + tree_columns = columns2Tree(columns) + for column in tree_columns.root.childs: + # Get child model is column is dotted notation + ma_sqla_fields_override[column.data] = self._column2field( + _datamodel, + column, + nested, + enum_dump_by_name + ) + _columns.append(column.data) + for k, v in ma_sqla_fields_override.items(): + setattr(SchemaMixin, k, v) + return self._meta_schema_factory(_columns, _model, SchemaMixin)() diff --git a/flask_appbuilder/api/manager.py b/flask_appbuilder/api/manager.py new file mode 100644 index 000000000..6aa62681f --- /dev/null +++ b/flask_appbuilder/api/manager.py @@ -0,0 +1,81 @@ +from apispec import APISpec +from apispec.ext.marshmallow import MarshmallowPlugin +from flask import current_app +from flask_appbuilder.api import BaseApi +from flask_appbuilder.api import expose, protect, safe +from flask_appbuilder.basemanager import BaseManager +from flask_appbuilder.baseviews import BaseView +from flask_appbuilder.security.decorators import has_access + + +class OpenApi(BaseApi): + route_base = '/api' + allow_browser_login = True + + @expose('//_openapi') + @protect() + @safe + def get(self, version): + """ Endpoint that renders an OpenApi spec for all views that belong + to a certain version + --- + get: + parameters: + - in: path + schema: + type: string + name: version + responses: + 200: + description: Item from Model + content: + application/json: + schema: + type: object + 404: + $ref: '#/components/responses/404' + 500: + $ref: '#/components/responses/500' + """ + version_found = False + api_spec = self._create_api_spec(version) + for base_api in current_app.appbuilder.baseviews: + if isinstance(base_api, BaseApi) and base_api.version == version: + base_api.add_api_spec(api_spec) + version_found = True + if version_found: + return self.response(200, **api_spec.to_dict()) + else: + return self.response_404() + + @staticmethod + def _create_api_spec(version): + return APISpec( + title=current_app.appbuilder.app_name, + version=version, + openapi_version="3.0.2", + info=dict(description=current_app.appbuilder.app_name), + plugins=[MarshmallowPlugin()], + servers=[{'url': "/api/{}".format(version)}] + ) + + +class SwaggerView(BaseView): + + default_view = 'ui' + openapi_uri = '/api/{}/_openapi' + + @expose('/') + @has_access + def show(self, version): + return self.render_template( + 'appbuilder/swagger/swagger.html', + openapi_uri=self.openapi_uri.format(version) + ) + + +class OpenApiManager(BaseManager): + def register_views(self): + self.appbuilder.add_api(OpenApi) + if self.appbuilder.get_app.config.get('FAB_API_SWAGGER_UI', False): + self.appbuilder.add_view_no_menu(SwaggerView) diff --git a/flask_appbuilder/api/schemas.py b/flask_appbuilder/api/schemas.py new file mode 100644 index 000000000..4b40441a1 --- /dev/null +++ b/flask_appbuilder/api/schemas.py @@ -0,0 +1,114 @@ +from ..const import ( + API_ADD_COLUMNS_RIS_KEY, + API_ADD_TITLE_RIS_KEY, + API_DESCRIPTION_COLUMNS_RIS_KEY, + API_EDIT_COLUMNS_RIS_KEY, + API_EDIT_TITLE_RIS_KEY, + API_FILTERS_RIS_KEY, + API_LABEL_COLUMNS_RIS_KEY, + API_LIST_COLUMNS_RIS_KEY, + API_LIST_TITLE_RIS_KEY, + API_ORDER_COLUMN_RIS_KEY, + API_ORDER_COLUMNS_RIS_KEY, + API_ORDER_DIRECTION_RIS_KEY, + API_PAGE_INDEX_RIS_KEY, + API_PAGE_SIZE_RIS_KEY, + API_PERMISSIONS_RIS_KEY, + API_SELECT_COLUMNS_RIS_KEY, + API_SELECT_KEYS_RIS_KEY, + API_SHOW_COLUMNS_RIS_KEY, + API_SHOW_TITLE_RIS_KEY, +) + +get_list_schema = { + "type": "object", + "properties": { + API_SELECT_KEYS_RIS_KEY: { + "type": "array", + "items": { + "type": "string", + "enum": [ + API_LIST_COLUMNS_RIS_KEY, + API_ORDER_COLUMNS_RIS_KEY, + API_LABEL_COLUMNS_RIS_KEY, + API_DESCRIPTION_COLUMNS_RIS_KEY, + API_LIST_TITLE_RIS_KEY, + "none", + ], + }, + }, + API_SELECT_COLUMNS_RIS_KEY: {"type": "array", "items": {"type": "string"}}, + API_ORDER_COLUMN_RIS_KEY: {"type": "string"}, + API_ORDER_DIRECTION_RIS_KEY: {"type": "string", "enum": ["asc", "desc"]}, + API_PAGE_INDEX_RIS_KEY: {"type": "integer"}, + API_PAGE_SIZE_RIS_KEY: {"type": "integer"}, + API_FILTERS_RIS_KEY: { + "type": "array", + "items": { + "type": "object", + "properties": { + "col": {"type": "string"}, + "opr": {"type": "string"}, + "value": { + "anyOf": [ + {"type": "number"}, + {"type": "string"}, + {"type": "boolean"}, + ] + }, + }, + }, + }, + }, +} + +get_item_schema = { + "type": "object", + "properties": { + API_SELECT_KEYS_RIS_KEY: { + "type": "array", + "items": { + "type": "string", + "enum": [ + API_SHOW_COLUMNS_RIS_KEY, + API_DESCRIPTION_COLUMNS_RIS_KEY, + API_LABEL_COLUMNS_RIS_KEY, + API_SHOW_TITLE_RIS_KEY, + "none", + ], + }, + }, + API_SELECT_COLUMNS_RIS_KEY: {"type": "array", "items": {"type": "string"}}, + }, +} + +get_info_schema = { + "type": "object", + "properties": { + API_SELECT_KEYS_RIS_KEY: { + "type": "array", + "items": { + "type": "string", + "enum": [ + API_ADD_COLUMNS_RIS_KEY, + API_EDIT_COLUMNS_RIS_KEY, + API_FILTERS_RIS_KEY, + API_PERMISSIONS_RIS_KEY, + API_ADD_TITLE_RIS_KEY, + API_EDIT_TITLE_RIS_KEY, + "none", + ], + }, + }, + API_ADD_COLUMNS_RIS_KEY: { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + API_PAGE_SIZE_RIS_KEY: {"type": "integer"}, + API_PAGE_INDEX_RIS_KEY: {"type": "integer"}, + }, + }, + }, + }, +} diff --git a/flask_appbuilder/babel/manager.py b/flask_appbuilder/babel/manager.py index 1af2bfb79..b3403da0d 100644 --- a/flask_appbuilder/babel/manager.py +++ b/flask_appbuilder/babel/manager.py @@ -1,5 +1,5 @@ import os -from flask import session, has_request_context +from flask import session, has_request_context, request from flask_babel import Babel from ..basemanager import BaseManager from .views import LocaleView @@ -35,6 +35,10 @@ def babel_default_locale(self): def get_locale(self): if has_request_context(): + # locale selector for API searches for request args + for arg, value in request.args.items(): + if arg=="_l_": + return value locale = session.get('locale') if locale: return locale diff --git a/flask_appbuilder/base.py b/flask_appbuilder/base.py index 3444c07ed..5dc6805ea 100644 --- a/flask_appbuilder/base.py +++ b/flask_appbuilder/base.py @@ -1,8 +1,8 @@ import logging - from flask import Blueprint, url_for, current_app from .views import IndexView, UtilView from .filters import TemplateFilters +from .api.manager import OpenApiManager from .menu import Menu from .babel.manager import BabelManager from .version import VERSION_STRING @@ -78,6 +78,8 @@ class AppBuilder(object): sm = None # Babel Manager Class bm = None + # OpenAPI Manager Class + openapi_manager = None # dict with addon name has key and intantiated class has value addon_managers = None # temporary list that hold addon_managers config key @@ -130,8 +132,8 @@ def __init__(self, app=None, self.static_folder = static_folder self.static_url_path = static_url_path self.update_perms = update_perms - self.app = app + if app is not None: self.init_app(app, session) @@ -148,7 +150,8 @@ def init_app(self, app, session): app.config.setdefault('APP_ICON', '') app.config.setdefault('LANGUAGES', {'en': {'flag': 'gb', 'name': 'English'}}) - app.config.setdefault('ADDON_MANAGERS',[]) + app.config.setdefault('ADDON_MANAGERS', []) + app.config.setdefault('FAB_API_MAX_PAGE_SIZE', 20) if self.security_manager_class is None: from flask_appbuilder.security.sqla.manager import SecurityManager self.security_manager_class = SecurityManager @@ -156,6 +159,7 @@ def init_app(self, app, session): self.session = session self.sm = self.security_manager_class(self) self.bm = BabelManager(self) + self.openapi_manager = OpenApiManager(self) self._add_global_static() self._add_global_filters() app.before_request(self.sm.before_request) @@ -174,6 +178,7 @@ def init_app(self, app, session): self._init_extension(app) def _init_extension(self, app): + app.appbuilder = self if not hasattr(app, 'extensions'): app.extensions = {} app.extensions['appbuilder'] = self @@ -258,6 +263,7 @@ def _add_admin_views(self): self.add_view_no_menu(UtilView()) self.bm.register_views() self.sm.register_views() + self.openapi_manager.register_views() def _add_addon_views(self): """ @@ -411,8 +417,8 @@ def add_view_no_menu(self, baseview, endpoint=None, static_folder=None): """ Add your views without creating a menu. - :param baseview: - A BaseView type class instantiated. + :param baseview: + A BaseView type class instantiated. """ baseview = self._check_and_init(baseview) @@ -430,6 +436,15 @@ def add_view_no_menu(self, baseview, endpoint=None, static_folder=None): log.warning(LOGMSG_WAR_FAB_VIEW_EXISTS.format(baseview.__class__.__name__)) return baseview + def add_api(self, baseview): + """ + Add a BaseApi class or child to AppBuilder + + :param baseview: A BaseApi type class + :return: The instantiated base view + """ + return self.add_view_no_menu(baseview) + def security_cleanup(self): """ This method is useful if you have changed @@ -487,5 +502,3 @@ def _process_inner_views(self): for v in self.baseviews: if isinstance(v, inner_class) and v not in view.get_init_inner_views(): view.get_init_inner_views().append(v) - - diff --git a/flask_appbuilder/baseapp.py b/flask_appbuilder/baseapp.py deleted file mode 100644 index ca94169ba..000000000 --- a/flask_appbuilder/baseapp.py +++ /dev/null @@ -1,6 +0,0 @@ -from .base import AppBuilder - -""" - This is for retro compatibility -""" -BaseApp = AppBuilder diff --git a/flask_appbuilder/console.py b/flask_appbuilder/console.py index 8e32c963b..cd2c4d57b 100644 --- a/flask_appbuilder/console.py +++ b/flask_appbuilder/console.py @@ -25,6 +25,7 @@ MONGOENGIE_REPO_URL = 'https://github.com/dpgaspar/Flask-AppBuilder-Skeleton-me/archive/master.zip' ADDON_REPO_URL = 'https://github.com/dpgaspar/Flask-AppBuilder-Skeleton-AddOn/archive/master.zip' + def import_application(app_package, appbuilder): sys.path.append(os.getcwd()) try: @@ -35,7 +36,7 @@ def import_application(app_package, appbuilder): if hasattr(_app, appbuilder): return getattr(_app, appbuilder) else: - click.echo(click.style('There in no appbuilder var on your package, you can use appbuilder parameter to config', fg='red')) + click.echo(click.style('There is no appbuilder var on your package, you can use appbuilder parameter to config', fg='red')) exit(3) diff --git a/flask_appbuilder/const.py b/flask_appbuilder/const.py index 7d82d109e..3e0598cae 100644 --- a/flask_appbuilder/const.py +++ b/flask_appbuilder/const.py @@ -107,7 +107,6 @@ """ Inform that view class was added, format with class name, name""" - FLAMSG_ERR_SEC_ACCESS_DENIED = lazy_gettext("Access is Denied") """ Access denied flash message """ @@ -121,3 +120,58 @@ AUTH_REMOTE_USER = 3 AUTH_OAUTH = 4 """ Constants for supported authentication types """ + +#----------------------------------- +# REST API Constants +#----------------------------------- + +API_SECURITY_VERSION = 'v1' +API_SECURITY_PROVIDER_DB = 'db' +API_SECURITY_PROVIDER_LDAP = 'ldap' +API_SECURITY_USERNAME_KEY = 'username' +API_SECURITY_PASSWORD_KEY = 'password' +API_SECURITY_PROVIDER_KEY = 'provider' +API_SECURITY_REFRESH_KEY = 'refresh' +API_SECURITY_ACCESS_TOKEN_KEY = 'access_token' +API_SECURITY_REFRESH_TOKEN_KEY = 'refresh_token' +# Response keys + +API_ORDER_COLUMNS_RES_KEY = 'order_columns' +API_LABEL_COLUMNS_RES_KEY = 'label_columns' +API_LIST_COLUMNS_RES_KEY = 'list_columns' +API_SHOW_COLUMNS_RES_KEY = 'show_columns' +API_ADD_COLUMNS_RES_KEY = 'add_columns' +API_EDIT_COLUMNS_RES_KEY = 'edit_columns' +API_DESCRIPTION_COLUMNS_RES_KEY = 'description_columns' +API_RESULT_RES_KEY = 'result' +API_FILTERS_RES_KEY = 'filters' +API_PERMISSIONS_RES_KEY = 'permissions' + +API_LIST_TITLE_RES_KEY = 'list_title' +API_ADD_TITLE_RES_KEY = 'add_title' +API_EDIT_TITLE_RES_KEY = 'edit_title' +API_SHOW_TITLE_RES_KEY = 'show_title' + +# Request Rison keys + +API_URI_RIS_KEY = 'q' +API_ORDER_COLUMNS_RIS_KEY = 'order_columns' +API_LABEL_COLUMNS_RIS_KEY = 'label_columns' +API_LIST_COLUMNS_RIS_KEY = 'list_columns' +API_SHOW_COLUMNS_RIS_KEY = 'show_columns' +API_ADD_COLUMNS_RIS_KEY = 'add_columns' +API_EDIT_COLUMNS_RIS_KEY = 'edit_columns' +API_DESCRIPTION_COLUMNS_RIS_KEY = 'description_columns' +API_FILTERS_RIS_KEY = 'filters' +API_PERMISSIONS_RIS_KEY = 'permissions' +API_SELECT_COLUMNS_RIS_KEY = 'columns' +API_SELECT_KEYS_RIS_KEY = 'keys' +API_ORDER_COLUMN_RIS_KEY = 'order_column' +API_ORDER_DIRECTION_RIS_KEY = 'order_direction' +API_PAGE_INDEX_RIS_KEY = 'page' +API_PAGE_SIZE_RIS_KEY = 'page_size' + +API_LIST_TITLE_RIS_KEY = 'list_title' +API_ADD_TITLE_RIS_KEY = 'add_title' +API_EDIT_TITLE_RIS_KEY = 'edit_title' +API_SHOW_TITLE_RIS_KEY = 'show_title' diff --git a/flask_appbuilder/models/filters.py b/flask_appbuilder/models/filters.py index d533fa9e8..728f36cbd 100644 --- a/flask_appbuilder/models/filters.py +++ b/flask_appbuilder/models/filters.py @@ -4,6 +4,9 @@ log = logging.getLogger(__name__) +map_args_filter = {} +""" private map for arg_name and child Filter classes """ + class BaseFilter(object): """ @@ -20,6 +23,13 @@ class BaseFilter(object): If true this filter was not set by the user """ + arg_name = None + """ + the request argument that represent the filter + child Filter classes should set it to enable + REST API use + """ + def __init__(self, column_name, datamodel, is_related_view=False): """ Constructor. @@ -35,6 +45,8 @@ def __init__(self, column_name, datamodel, is_related_view=False): self.datamodel = datamodel self.model = datamodel.obj self.is_related_view = is_related_view + if self.arg_name: + map_args_filter[self.arg_name] = self.__class__ def apply(self, query, value): """ @@ -108,12 +120,12 @@ def __init__(self, filter_converter, datamodel, search_columns=None): :param search_columns: restricts possible columns, accepts a list of column names :param datamodel: Accepts BaseInterface class """ - search_columns = search_columns or [] + self.search_columns = search_columns or [] self.filter_converter = filter_converter self.datamodel = datamodel self.clear_filters() - if search_columns: - self._search_filters = self._get_filters(search_columns) + if self.search_columns: + self._search_filters = self._get_filters(self.search_columns) self._all_filters = self._get_filters(datamodel.get_columns_list()) def get_search_filters(self): @@ -138,6 +150,19 @@ def _add_filter(self, filter_instance, value): def add_filter_index(self, column_name, filter_instance_index, value): self._add_filter(self._all_filters[column_name][filter_instance_index], value) + def rest_add_filters(self, data): + """ + Adds list of dicts + + :param data: list of dicts + :return: + """ + for _filter in data: + filter_class = map_args_filter.get(_filter['opr'], None) + if filter_class: + self.add_filter(_filter['col'], filter_class, + _filter['value']) + def add_filter(self, column_name, filter_class, value): self._add_filter(filter_class(column_name, self.datamodel), value) return self @@ -208,7 +233,7 @@ def apply_all(self, query): return query def __repr__(self): - retstr = "FILTERS \n" + retstr = "FILTERS:" for flt, value in self.get_filters_values(): retstr = retstr + "%s.%s:%s\n" % (flt.model.__table__, str(flt.column_name), str(value)) return retstr diff --git a/flask_appbuilder/models/sqla/filters.py b/flask_appbuilder/models/sqla/filters.py index 091322a3c..9d6df1a49 100644 --- a/flask_appbuilder/models/sqla/filters.py +++ b/flask_appbuilder/models/sqla/filters.py @@ -2,7 +2,7 @@ import datetime from dateutil import parser from flask_babel import lazy_gettext -from ..filters import BaseFilter, FilterRelation, BaseFilterConverter +from ..filters import BaseFilter, FilterRelation, BaseFilterConverter, map_args_filter log = logging.getLogger(__name__) @@ -12,6 +12,7 @@ 'FilterRelationManyToManyEqual', 'FilterRelationOneToManyEqual', 'FilterRelationOneToManyNotEqual', 'FilterSmaller'] + def get_field_setup_query(query, model, column_name): """ Help function for SQLA filters, checks for dot notation on column names. @@ -58,6 +59,7 @@ def set_value_to_type(datamodel, column_name, value): class FilterStartsWith(BaseFilter): name = lazy_gettext('Starts with') + arg_name = 'sw' def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -66,6 +68,7 @@ def apply(self, query, value): class FilterNotStartsWith(BaseFilter): name = lazy_gettext('Not Starts with') + arg_name = "nsw" def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -74,6 +77,7 @@ def apply(self, query, value): class FilterEndsWith(BaseFilter): name = lazy_gettext('Ends with') + arg_name = "ew" def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -82,7 +86,8 @@ def apply(self, query, value): class FilterNotEndsWith(BaseFilter): name = lazy_gettext('Not Ends with') - + arg_name = 'new' + def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) return query.filter(~field.like('%' + value)) @@ -90,6 +95,7 @@ def apply(self, query, value): class FilterContains(BaseFilter): name = lazy_gettext('Contains') + arg_name = 'ct' def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -98,6 +104,7 @@ def apply(self, query, value): class FilterNotContains(BaseFilter): name = lazy_gettext('Not Contains') + arg_name = 'nct' def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -106,6 +113,7 @@ def apply(self, query, value): class FilterEqual(BaseFilter): name = lazy_gettext('Equal to') + arg_name = 'eq' def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -115,6 +123,7 @@ def apply(self, query, value): class FilterNotEqual(BaseFilter): name = lazy_gettext('Not Equal to') + arg_name = 'neq' def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -124,6 +133,7 @@ def apply(self, query, value): class FilterGreater(BaseFilter): name = lazy_gettext('Greater than') + arg_name = 'gt' def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -133,6 +143,7 @@ def apply(self, query, value): class FilterSmaller(BaseFilter): name = lazy_gettext('Smaller than') + arg_name = 'lt' def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -142,6 +153,7 @@ def apply(self, query, value): class FilterRelationOneToManyEqual(FilterRelation): name = lazy_gettext('Relation') + arg_name = 'rel_o_m' def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -151,6 +163,7 @@ def apply(self, query, value): class FilterRelationOneToManyNotEqual(FilterRelation): name = lazy_gettext('No Relation') + arg_name = 'nrel_o_m' def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -160,6 +173,7 @@ def apply(self, query, value): class FilterRelationManyToManyEqual(FilterRelation): name = lazy_gettext('Relation as Many') + arg_name = 'rel_m_m' def apply(self, query, value): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -169,6 +183,7 @@ def apply(self, query, value): class FilterEqualFunction(BaseFilter): name = "Filter view with a function" + arg_name = 'eqf' def apply(self, query, func): query, field = get_field_setup_query(query, self.model, self.column_name) @@ -177,6 +192,7 @@ def apply(self, query, func): class FilterInFunction(BaseFilter): name = "Filter view where field is in a list returned by a function" + arg_name = 'inf' def apply(self, query, func): query, field = get_field_setup_query(query, self.model, self.column_name) diff --git a/flask_appbuilder/models/sqla/interface.py b/flask_appbuilder/models/sqla/interface.py index 3e4b1fc16..9b76f74a5 100644 --- a/flask_appbuilder/models/sqla/interface.py +++ b/flask_appbuilder/models/sqla/interface.py @@ -4,10 +4,10 @@ import sqlalchemy as sa from . import filters -from sqlalchemy.orm import joinedload +from sqlalchemy.orm import joinedload, Load from sqlalchemy.exc import IntegrityError from sqlalchemy import func -from sqlalchemy.orm.properties import SynonymProperty +from sqlalchemy.orm.descriptor_props import SynonymProperty from ..base import BaseInterface from ..group import GroupByDateYear, GroupByDateMonth, GroupByCol @@ -25,6 +25,7 @@ def _include_filters(obj): if not hasattr(obj, key): setattr(obj, key, getattr(filters, key)) + def _is_sqla_type(obj, sa_type): return isinstance(obj, sa_type) or \ isinstance(obj, sa.types.TypeDecorator) and isinstance(obj.impl, sa_type) @@ -62,7 +63,12 @@ def model_name(self): """ return self.obj.__name__ - def _get_base_query(self, query=None, filters=None, order_column='', order_direction=''): + @staticmethod + def is_model_already_joinded(query, model): + return model in [mapper.class_ for mapper in query._join_entities] + + def _get_base_query(self, query=None, filters=None, + order_column='', order_direction=''): if filters: query = filters.apply_all(query) if order_column != '': @@ -77,8 +83,34 @@ def _get_base_query(self, query=None, filters=None, order_column='', order_direc query = query.order_by(self._get_attr(order_column).desc()) return query + def _query_select_options(self, query, select_columns=None): + """ + Add select load options to query. The goal + is to only SQL select what is requested + + :param query: SQLAlchemy Query obj + :param select_columns: (list) of columns + :return: SQLAlchemy Query obj + """ + if select_columns: + _load_options = list() + for column in select_columns: + if '.' in column: + model_relation = self.get_related_model(column.split('.')[0]) + if not self.is_model_already_joinded(query, model_relation): + query = query.join(model_relation) + _load_options.append(Load(model_relation).load_only(column.split('.')[1])) + else: + if (not self.is_relation(column) and + not hasattr(getattr(self.obj, column), '__call__')): + _load_options.append(Load(self.obj).load_only(column)) + else: + _load_options.append(Load(self.obj)) + query = query.options(*tuple(_load_options)) + return query + def query(self, filters=None, order_column='', order_direction='', - page=None, page_size=None): + page=None, page_size=None, select_columns=None): """ QUERY :param filters: @@ -94,11 +126,13 @@ def query(self, filters=None, order_column='', order_direction='', """ query = self.session.query(self.obj) + query = self._query_select_options(query, select_columns) if len(order_column.split('.')) >= 2: tmp_order_column = '' for join_relation in order_column.split('.')[:-1]: model_relation = self.get_related_model(join_relation) - query = query.join(model_relation) + if not self.is_model_already_joinded(query, model_relation): + query = query.join(model_relation) # redefine order column name, because relationship can have a different name # from the related table name. tmp_order_column = tmp_order_column + model_relation.__tablename__ + '.' @@ -118,7 +152,6 @@ def query(self, filters=None, order_column='', order_direction='', query = query.offset(page * page_size) if page_size: query = query.limit(page_size) - return count, query.all() def query_simple_group(self, group_by='', aggregate_func=None, aggregate_col=None, filters=None): @@ -265,7 +298,7 @@ def is_nullable(self, col_name): def is_unique(self, col_name): try: - return self.list_columns[col_name].unique + return self.list_columns[col_name].unique == True except: return False @@ -302,7 +335,7 @@ def get_max_length(self, col_name): ------------------------------- """ - def add(self, item): + def add(self, item, raise_exception=False): try: self.session.add(item) self.session.commit() @@ -312,14 +345,18 @@ def add(self, item): self.message = (as_unicode(self.add_integrity_error_message), 'warning') log.warning(LOGMSG_WAR_DBI_ADD_INTEGRITY.format(str(e))) self.session.rollback() + if raise_exception: + raise e return False except Exception as e: self.message = (as_unicode(self.general_error_message + ' ' + str(sys.exc_info()[0])), 'danger') log.exception(LOGMSG_ERR_DBI_ADD_GENERIC.format(str(e))) self.session.rollback() + if raise_exception: + raise e return False - def edit(self, item): + def edit(self, item, raise_exception=False): try: self.session.merge(item) self.session.commit() @@ -329,14 +366,18 @@ def edit(self, item): self.message = (as_unicode(self.edit_integrity_error_message), 'warning') log.warning(LOGMSG_WAR_DBI_EDIT_INTEGRITY.format(str(e))) self.session.rollback() + if raise_exception: + raise e return False except Exception as e: self.message = (as_unicode(self.general_error_message + ' ' + str(sys.exc_info()[0])), 'danger') log.exception(LOGMSG_ERR_DBI_EDIT_GENERIC.format(str(e))) self.session.rollback() + if raise_exception: + raise e return False - def delete(self, item): + def delete(self, item, raise_exception=False): try: self._delete_files(item) self.session.delete(item) @@ -347,11 +388,15 @@ def delete(self, item): self.message = (as_unicode(self.delete_integrity_error_message), 'warning') log.warning(LOGMSG_WAR_DBI_DEL_INTEGRITY.format(str(e))) self.session.rollback() + if raise_exception: + raise e return False except Exception as e: self.message = (as_unicode(self.general_error_message + ' ' + str(sys.exc_info()[0])), 'danger') log.exception(LOGMSG_ERR_DBI_DEL_GENERIC.format(str(e))) self.session.rollback() + if raise_exception: + raise e return False def delete_all(self, items): diff --git a/flask_appbuilder/security/api.py b/flask_appbuilder/security/api.py new file mode 100644 index 000000000..052d82603 --- /dev/null +++ b/flask_appbuilder/security/api.py @@ -0,0 +1,137 @@ +from flask import request +from flask_jwt_extended import ( + create_access_token, + create_refresh_token, + get_jwt_identity, + jwt_refresh_token_required, +) + +from ..api import BaseApi, safe +from ..const import ( + API_SECURITY_ACCESS_TOKEN_KEY, + API_SECURITY_PASSWORD_KEY, + API_SECURITY_PROVIDER_DB, + API_SECURITY_PROVIDER_KEY, + API_SECURITY_PROVIDER_LDAP, + API_SECURITY_REFRESH_KEY, + API_SECURITY_REFRESH_TOKEN_KEY, + API_SECURITY_USERNAME_KEY, + API_SECURITY_VERSION, +) +from ..views import expose + + +class SecurityApi(BaseApi): + + resource_name = "security" + version = API_SECURITY_VERSION + + def add_apispec_components(self, api_spec): + super(SecurityApi, self).add_apispec_components(api_spec) + jwt_scheme = {"type": "http", "scheme": "bearer", "bearerFormat": "JWT"} + api_spec.components.security_scheme("jwt", jwt_scheme) + + @expose("/login", methods=["POST"]) + @safe + def login(self): + """Login endpoint for the API returns a JWT and optionally a refresh token + --- + post: + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + username: + type: string + password: + type: string + provider: + type: string + enum: + - db + - ldap + refresh: + type: boolean + responses: + 200: + description: Authentication Successful + content: + application/json: + schema: + type: object + properties: + access_token: + type: string + refresh_token: + type: string + 400: + $ref: '#/components/responses/400' + 401: + $ref: '#/components/responses/401' + 500: + $ref: '#/components/responses/500' + """ + if not request.is_json: + return self.response_400(message="Request payload is not JSON") + username = request.json.get(API_SECURITY_USERNAME_KEY, None) + password = request.json.get(API_SECURITY_PASSWORD_KEY, None) + provider = request.json.get(API_SECURITY_PROVIDER_KEY, None) + refresh = request.json.get(API_SECURITY_REFRESH_KEY, False) + if not username or not password or not provider: + return self.response_400(message="Missing required parameter") + # AUTH + if provider == API_SECURITY_PROVIDER_DB: + user = self.appbuilder.sm.auth_user_db(username, password) + elif provider == API_SECURITY_PROVIDER_LDAP: + user = self.appbuilder.sm.auth_user_ldap(username, password) + else: + return self.response_400( + message="Provider {} not supported".format(provider) + ) + if not user: + return self.response_401() + + # Identity can be any data that is json serializable + resp = dict() + resp[API_SECURITY_ACCESS_TOKEN_KEY] = create_access_token( + identity=user.id, fresh=True + ) + if refresh: + resp[API_SECURITY_REFRESH_TOKEN_KEY] = create_refresh_token( + identity=user.id + ) + return self.response(200, **resp) + + @expose("/refresh", methods=["POST"]) + @jwt_refresh_token_required + @safe + def refresh(self): + """ + Security endpoint for the refresh token, so we can obtain a new + token without forcing the user to login again + --- + post: + responses: + 200: + description: Refresh Successful + content: + application/json: + schema: + type: object + properties: + refresh_token: + type: string + 401: + $ref: '#/components/responses/401' + 500: + $ref: '#/components/responses/500' + """ + resp = { + API_SECURITY_REFRESH_TOKEN_KEY: create_access_token( + identity=get_jwt_identity(), fresh=False + ) + } + return self.response(200, **resp) diff --git a/flask_appbuilder/security/decorators.py b/flask_appbuilder/security/decorators.py index f1cbea005..f93989086 100644 --- a/flask_appbuilder/security/decorators.py +++ b/flask_appbuilder/security/decorators.py @@ -1,13 +1,85 @@ import logging import functools -from flask import flash, redirect, url_for, make_response, jsonify, request +from flask import ( + flash, + redirect, + url_for, + make_response, + jsonify, + request, + current_app +) +from flask_jwt_extended import verify_jwt_in_request from .._compat import as_unicode from ..const import LOGMSG_ERR_SEC_ACCESS_DENIED, FLAMSG_ERR_SEC_ACCESS_DENIED, PERMISSION_PREFIX log = logging.getLogger(__name__) - + +def protect(allow_browser_login=False): + """ + Use this decorator to enable granular security permissions + to your API methods (BaseApi and child classes). + Permissions will be associated to a role, and roles are associated to users. + + allow_browser_login will accept signed cookies obtained from the normal MVC app:: + + class MyApi(BaseApi): + @expose('/dosonmething', methods=['GET']) + @protect(allow_browser_login=True) + @safe + def do_something(self): + .... + + @expose('/dosonmethingelse', methods=['GET']) + @protect() + @safe + def do_something_else(self): + .... + + By default the permission's name is the methods name. + """ + def _protect(f): + if hasattr(f, '_permission_name'): + permission_str = f._permission_name + else: + permission_str = f.__name__ + + def wraps(self, *args, **kwargs): + permission_str = "{}{}".format(PERMISSION_PREFIX, f._permission_name) + class_permission_name = self.__class__.__name__ + if current_app.appbuilder.sm.is_item_public( + permission_str, + class_permission_name + ): + return f(self, *args, **kwargs) + if not (self.allow_browser_login or allow_browser_login): + verify_jwt_in_request() + if current_app.appbuilder.sm.has_access( + permission_str, + class_permission_name + ): + return f(self, *args, **kwargs) + elif (self.allow_browser_login or allow_browser_login): + verify_jwt_in_request() + if current_app.appbuilder.sm.has_access( + permission_str, + class_permission_name + ): + return f(self, *args, **kwargs) + log.warning( + LOGMSG_ERR_SEC_ACCESS_DENIED.format( + permission_str, + class_permission_name + ) + ) + return self.response_401() + f._permission_name = permission_str + return functools.update_wrapper(wraps, f) + return _protect + + def has_access(f): """ Use this decorator to enable granular security permissions to your methods. @@ -22,12 +94,25 @@ def has_access(f): def wraps(self, *args, **kwargs): permission_str = PERMISSION_PREFIX + f._permission_name - if self.appbuilder.sm.has_access(permission_str, self.__class__.__name__): + if self.appbuilder.sm.has_access( + permission_str, + self.__class__.__name__ + ): return f(self, *args, **kwargs) else: - log.warning(LOGMSG_ERR_SEC_ACCESS_DENIED.format(permission_str, self.__class__.__name__)) + log.warning( + LOGMSG_ERR_SEC_ACCESS_DENIED.format( + permission_str, + self.__class__.__name__ + ) + ) flash(as_unicode(FLAMSG_ERR_SEC_ACCESS_DENIED), "danger") - return redirect(url_for(self.appbuilder.sm.auth_view.__class__.__name__ + ".login", next=request.url)) + return redirect( + url_for( + self.appbuilder.sm.auth_view.__class__.__name__ + ".login", + next=request.url + ) + ) f._permission_name = permission_str return functools.update_wrapper(wraps, f) @@ -48,15 +133,29 @@ def has_access_api(f): def wraps(self, *args, **kwargs): permission_str = PERMISSION_PREFIX + f._permission_name - if self.appbuilder.sm.has_access(permission_str, self.__class__.__name__): + if self.appbuilder.sm.has_access( + permission_str, + self.__class__.__name__ + ): return f(self, *args, **kwargs) else: - log.warning(LOGMSG_ERR_SEC_ACCESS_DENIED.format(permission_str, self.__class__.__name__)) - response = make_response(jsonify({'message': str(FLAMSG_ERR_SEC_ACCESS_DENIED), - 'severity': 'danger'}), 401) + log.warning( + LOGMSG_ERR_SEC_ACCESS_DENIED.format( + permission_str, + self.__class__.__name__ + ) + ) + response = make_response( + jsonify( + { + 'message': str(FLAMSG_ERR_SEC_ACCESS_DENIED), + 'severity': 'danger' + } + ), + 401 + ) response.headers['Content-Type'] = "application/json" return response - return redirect(url_for(self.appbuilder.sm.auth_view.__class__.__name__ + ".login", next=request.url)) f._permission_name = permission_str return functools.update_wrapper(wraps, f) diff --git a/flask_appbuilder/security/manager.py b/flask_appbuilder/security/manager.py index cabfc5fe2..86c485594 100644 --- a/flask_appbuilder/security/manager.py +++ b/flask_appbuilder/security/manager.py @@ -6,21 +6,46 @@ from flask import url_for, g, session from werkzeug.security import generate_password_hash, check_password_hash from flask_login import LoginManager, current_user +from flask_jwt_extended import JWTManager +from flask_jwt_extended import current_user as current_user_jwt from flask_openid import OpenID from flask_babel import lazy_gettext as _ -from .views import AuthDBView, AuthOIDView, ResetMyPasswordView, AuthLDAPView, AuthOAuthView, AuthRemoteUserView, \ - ResetPasswordView, UserDBModelView, UserLDAPModelView, UserOIDModelView, UserOAuthModelView, UserRemoteUserModelView, \ - RoleModelView, PermissionViewModelView, ViewMenuModelView, PermissionModelView, UserStatsChartView, RegisterUserModelView, \ +from .api import SecurityApi +from .views import ( + AuthDBView, + AuthOIDView, + ResetMyPasswordView, + AuthLDAPView, + AuthOAuthView, + AuthRemoteUserView, + ResetPasswordView, + UserDBModelView, + UserLDAPModelView, + UserOIDModelView, + UserOAuthModelView, + UserRemoteUserModelView, + RoleModelView, + PermissionViewModelView, + ViewMenuModelView, + PermissionModelView, + UserStatsChartView, + RegisterUserModelView, UserInfoEditView +) from .registerviews import RegisterUserDBView, RegisterUserOIDView, RegisterUserOAuthView from ..basemanager import BaseManager -from ..const import AUTH_OID, AUTH_DB, AUTH_LDAP, \ - AUTH_REMOTE_USER, AUTH_OAUTH, \ - LOGMSG_ERR_SEC_AUTH_LDAP, \ - LOGMSG_ERR_SEC_AUTH_LDAP_TLS, \ - LOGMSG_WAR_SEC_NO_USER, \ - LOGMSG_WAR_SEC_NOLDAP_OBJ, \ - LOGMSG_WAR_SEC_LOGIN_FAILED +from ..const import ( + AUTH_OID, + AUTH_DB, + AUTH_LDAP, + AUTH_REMOTE_USER, + AUTH_OAUTH, + LOGMSG_ERR_SEC_AUTH_LDAP, + LOGMSG_ERR_SEC_AUTH_LDAP_TLS, + LOGMSG_WAR_SEC_NO_USER, + LOGMSG_WAR_SEC_NOLDAP_OBJ, + LOGMSG_WAR_SEC_LOGIN_FAILED +) log = logging.getLogger(__name__) @@ -36,7 +61,8 @@ def add_permissions_view(self, base_permissions, view_menu): Adds a permission on a view menu to the backend :param base_permissions: - list of permissions from view (all exposed methods): 'can_add','can_edit' etc... + list of permissions from view (all exposed methods): + 'can_add','can_edit' etc... :param view_menu: name of the view or menu to add """ @@ -159,6 +185,10 @@ class BaseSecurityManager(AbstractSecurityManager): userinfoeditview = UserInfoEditView """ Override if you want your own User information edit view """ + # API + security_api = SecurityApi + """ Override if you want your own Security API login endpoint """ + rolemodelview = RoleModelView permissionmodelview = PermissionModelView userstatschartview = UserStatsChartView @@ -179,7 +209,9 @@ def __init__(self, appbuilder): # LDAP Config if self.auth_type == AUTH_LDAP: if 'AUTH_LDAP_SERVER' not in app.config: - raise Exception("No AUTH_LDAP_SERVER defined on config with AUTH_LDAP authentication type.") + raise Exception( + "No AUTH_LDAP_SERVER defined on config with AUTH_LDAP authentication type." + ) app.config.setdefault('AUTH_LDAP_SEARCH', '') app.config.setdefault('AUTH_LDAP_SEARCH_FILTER', '') app.config.setdefault('AUTH_LDAP_BIND_USER', '') @@ -218,13 +250,21 @@ def __init__(self, appbuilder): self.oauth_whitelists[provider_name] = _provider['whitelist'] self.oauth_remotes[provider_name] = obj_provider + # Setup Flask-Login self.lm = LoginManager(app) self.lm.login_view = 'login' self.lm.user_loader(self.load_user) + # Setup Flask-Jwt-Extended + self.jwt_manager = JWTManager() + self.jwt_manager.init_app(app) + self.jwt_manager.user_loader_callback_loader(self.load_user) + @property def get_url_for_registeruser(self): - return url_for('%s.%s' % (self.registeruser_view.endpoint, self.registeruser_view.default_view)) + return url_for( + '%s.%s' % (self.registeruser_view.endpoint, self.registeruser_view.default_view) + ) @property def get_user_datamodel(self): @@ -360,7 +400,8 @@ def wraps(provider, response=None): ret = f(self, provider, response=response) # Checks if decorator is well behaved and returns a dict as supposed. if not type(ret) == dict: - log.error("OAuth user info decorated function did not returned a dict, but: {0}".format(type(ret))) + log.error( + "OAuth user info decorated function did not returned a dict, but: {0}".format(type(ret))) return {} return ret self.oauth_user_info = wraps @@ -396,7 +437,7 @@ def set_oauth_session(self, provider, oauth_response): # Save users token on encrypted session cookie session['oauth'] = ( oauth_response[token_key], - oauth_response.get(token_secret,'') + oauth_response.get(token_secret, '') ) session['oauth_provider'] = provider @@ -417,30 +458,43 @@ def get_oauth_user_info(self, provider, resp): return {'username': "twitter_" + me.data.get('screen_name', '')} # for linkedin if provider == 'linkedin': - me = self.appbuilder.sm.oauth_remotes[provider].get('people/~:(id,email-address,first-name,last-name)?format=json') + me = self.appbuilder.sm.oauth_remotes[provider].get( + 'people/~:(id,email-address,first-name,last-name)?format=json' + ) log.debug("User info from Linkedin: {0}".format(me.data)) - return {'username': "linkedin_" + me.data.get('id', ''), + return { + 'username': "linkedin_" + me.data.get('id', ''), 'email': me.data.get('email-address', ''), 'first_name': me.data.get('firstName', ''), - 'last_name': me.data.get('lastName', '')} + 'last_name': me.data.get('lastName', '') + } # for Google if provider == 'google': me = self.appbuilder.sm.oauth_remotes[provider].get('userinfo') log.debug("User info from Google: {0}".format(me.data)) - return {'username': "google_" + me.data.get('id', ''), + return { + 'username': "google_" + me.data.get('id', ''), 'first_name': me.data.get('given_name', ''), 'last_name': me.data.get('family_name', ''), - 'email': me.data.get('email', '')} + 'email': me.data.get('email', '') + } # for Azure AD Tenant. Azure OAuth response contains JWT token which has user info. # JWT token needs to be base64 decoded. # https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-code if provider == 'azure': log.debug("Azure response received : {0}".format(resp)) - id_token=resp['id_token'] + id_token = resp['id_token'] log.debug(str(id_token)) - me=self._azure_jwt_token_parse(id_token) + me = self._azure_jwt_token_parse(id_token) log.debug("Parse JWT token : {0}".format(me)) - return { 'name' : me['name'] , 'email' : me['upn'], 'first_name' : me['given_name'], 'last_name' : me['family_name'], 'id' : me['oid'], 'username' : me['oid'] } + return { + 'name': me['name'], + 'email': me['upn'], + 'first_name': me['given_name'], + 'last_name': me['family_name'], + 'id': me['oid'], + 'username': me['oid'] + } else: return {} @@ -448,14 +502,13 @@ def _azure_parse_jwt(self, id_token): jwt_token_parts = r"^([^\.\s]*)\.([^\.\s]+)\.([^\.\s]*)$" matches = re.search(jwt_token_parts, id_token) if not matches or len(matches.groups()) < 3: - log.error( 'Unable to parse token.') + log.error('Unable to parse token.') return {} - return { 'header': matches.group(1), 'Payload': matches.group(2), 'Sig': matches.group(3) - } + } def _azure_jwt_token_parse(self, id_token): jwt_split_token = self._azure_parse_jwt(id_token) @@ -469,15 +522,18 @@ def _azure_jwt_token_parse(self, id_token): decoded_payload = base64.urlsafe_b64decode(payload_b64_string.encode('ascii')) if not decoded_payload: - log.error( 'Payload of id_token could not be base64 url decoded.') + log.error('Payload of id_token could not be base64 url decoded.') return jwt_decoded_payload = json.loads(decoded_payload.decode('utf-8')) return jwt_decoded_payload - def register_views(self): + + # Security APIs + self.appbuilder.add_api(self.security_api) + if self.auth_user_registration: if self.auth_type == AUTH_DB: self.registeruser_view = self.registeruserdbview() @@ -515,37 +571,58 @@ def register_views(self): self.appbuilder.add_view_no_menu(self.auth_view) - self.user_view = self.appbuilder.add_view(self.user_view, "List Users", - icon="fa-user", label=_("List Users"), - category="Security", category_icon="fa-cogs", - category_label=_('Security')) + self.user_view = self.appbuilder.add_view( + self.user_view, "List Users", + icon="fa-user", label=_("List Users"), + category="Security", category_icon="fa-cogs", + category_label=_('Security') + ) - role_view = self.appbuilder.add_view(self.rolemodelview, "List Roles", - icon="fa-group", label=_('List Roles'), - category="Security", category_icon="fa-cogs") + role_view = self.appbuilder.add_view( + self.rolemodelview, + "List Roles", + icon="fa-group", + label=_('List Roles'), + category="Security", + category_icon="fa-cogs" + ) role_view.related_views = [self.user_view.__class__] - self.appbuilder.add_view(self.userstatschartview, - "User's Statistics", icon="fa-bar-chart-o", - label=_("User's Statistics"), - category="Security") - + self.appbuilder.add_view( + self.userstatschartview, + "User's Statistics", icon="fa-bar-chart-o", + label=_("User's Statistics"), + category="Security" + ) if self.auth_user_registration: - self.appbuilder.add_view(self.registerusermodelview, - "User's Statistics", icon="fa-user-plus", - label=_("User Registrations"), - category="Security") - + self.appbuilder.add_view( + self.registerusermodelview, + "User's Statistics", icon="fa-user-plus", + label=_("User Registrations"), + category="Security" + ) self.appbuilder.menu.add_separator("Security") - self.appbuilder.add_view(self.permissionmodelview, - "Base Permissions", icon="fa-lock", - label=_("Base Permissions"), category="Security") - self.appbuilder.add_view(self.viewmenumodelview, - "Views/Menus", icon="fa-list-alt", - label=_('Views/Menus'), category="Security") - self.appbuilder.add_view(self.permissionviewmodelview, - "Permission on Views/Menus", icon="fa-link", - label=_('Permission on Views/Menus'), category="Security") + self.appbuilder.add_view( + self.permissionmodelview, + "Base Permissions", + icon="fa-lock", + label=_("Base Permissions"), + category="Security" + ) + self.appbuilder.add_view( + self.viewmenumodelview, + "Views/Menus", + icon="fa-list-alt", + label=_('Views/Menus'), + category="Security" + ) + self.appbuilder.add_view( + self.permissionviewmodelview, + "Permission on Views/Menus", + icon="fa-link", + label=_('Permission on Views/Menus'), + category="Security" + ) def create_db(self): """ @@ -628,16 +705,27 @@ def _search_ldap(self, ldap, con, username): if self.auth_ldap_append_domain: username = username + '@' + self.auth_ldap_append_domain if self.auth_ldap_search_filter: - filter_str = "(&%s(%s=%s))" % (self.auth_ldap_search_filter, self.auth_ldap_uid_field, username) + filter_str = \ + "(&%s(%s=%s))" % ( + self.auth_ldap_search_filter, + self.auth_ldap_uid_field, username + ) else: - filter_str = "(%s=%s)" % (self.auth_ldap_uid_field, username) - user = con.search_s(self.auth_ldap_search, - ldap.SCOPE_SUBTREE, - filter_str, - [self.auth_ldap_firstname_field, - self.auth_ldap_lastname_field, - self.auth_ldap_email_field - ]) + filter_str = \ + "(%s=%s)" % ( + self.auth_ldap_uid_field, + username + ) + user = con.search_s( + self.auth_ldap_search, + ldap.SCOPE_SUBTREE, + filter_str, + [ + self.auth_ldap_firstname_field, + self.auth_ldap_lastname_field, + self.auth_ldap_email_field + ] + ) if user: if not user[0][0]: return None @@ -754,9 +842,21 @@ def auth_user_ldap(self, username, password): if self.auth_user_registration and user is None: user = self.add_user( username=username, - first_name=self.ldap_extract(ldap_user_info, self.auth_ldap_firstname_field, username), - last_name=self.ldap_extract(ldap_user_info, self.auth_ldap_lastname_field, username), - email=self.ldap_extract(ldap_user_info, self.auth_ldap_email_field, username + '@email.notfound'), + first_name=self.ldap_extract( + ldap_user_info, + self.auth_ldap_firstname_field, + username + ), + last_name=self.ldap_extract( + ldap_user_info, + self.auth_ldap_lastname_field, + username + ), + email=self.ldap_extract( + ldap_user_info, + self.auth_ldap_email_field, + username + '@email.notfound' + ), role=self.find_role(self.auth_user_registration_role) ) @@ -775,6 +875,7 @@ def auth_user_oid(self, email): """ OpenID user Authentication + :param email: user's email to authenticate :type self: User model """ user = self.find_user(email=email) @@ -789,6 +890,7 @@ def auth_user_remote_user(self, username): """ REMOTE_USER user Authentication + :param username: user's username for remote auth :type self: User model """ user = self.find_user(username=username) @@ -880,7 +982,8 @@ def _has_view_access(self, user, permission_name, view_name): permissions = role.permissions if permissions: for permission in permissions: - if (view_name == permission.view_menu.name) and (permission_name == permission.permission.name): + if ((view_name == permission.view_menu.name) + and (permission_name == permission.permission.name)): return True return False @@ -890,9 +993,33 @@ def has_access(self, permission_name, view_name): """ if current_user.is_authenticated: return self._has_view_access(g.user, permission_name, view_name) + elif current_user_jwt: + return self._has_view_access(current_user_jwt, permission_name, view_name) else: return self.is_item_public(permission_name, view_name) + @staticmethod + def get_user_permissions_on_view(view_name): + """ + Returns all current user permissions + on a certain view/resource + :param view_name: The name of the view/resource/menu + :return: (list) with permissions + """ + _ret = list() + if current_user.is_authenticated: + _current_user = current_user + elif current_user_jwt: + _current_user = current_user_jwt + else: + return _ret + for role in _current_user.roles: + if role.permissions: + for permission in role.permissions: + if permission.view_menu.name == view_name: + _ret.append(permission.permission.name) + return _ret + def add_permissions_view(self, base_permissions, view_menu): """ Adds a permission on a view menu to the backend diff --git a/flask_appbuilder/security/sqla/models.py b/flask_appbuilder/security/sqla/models.py index 09a8f3da9..e85741a9f 100644 --- a/flask_appbuilder/security/sqla/models.py +++ b/flask_appbuilder/security/sqla/models.py @@ -1,6 +1,7 @@ import datetime from flask import g -from sqlalchemy import Table, Column, Integer, String, Boolean, DateTime, ForeignKey, Sequence, UniqueConstraint +from sqlalchemy import Table, Column, Integer, \ + String, Boolean, DateTime, ForeignKey, Sequence, UniqueConstraint from sqlalchemy.orm import relationship, backref from sqlalchemy.ext.declarative import declared_attr from ... import Model @@ -46,11 +47,12 @@ def __repr__(self): return str(self.permission).replace('_', ' ') + ' on ' + str(self.view_menu) -assoc_permissionview_role = Table('ab_permission_view_role', Model.metadata, - Column('id', Integer, Sequence('ab_permission_view_role_id_seq'), primary_key=True), - Column('permission_view_id', Integer, ForeignKey('ab_permission_view.id')), - Column('role_id', Integer, ForeignKey('ab_role.id')), - UniqueConstraint('permission_view_id', 'role_id') +assoc_permissionview_role = Table( + 'ab_permission_view_role', Model.metadata, + Column('id', Integer, Sequence('ab_permission_view_role_id_seq'), primary_key=True), + Column('permission_view_id', Integer, ForeignKey('ab_permission_view.id')), + Column('role_id', Integer, ForeignKey('ab_role.id')), + UniqueConstraint('permission_view_id', 'role_id') ) @@ -59,17 +61,22 @@ class Role(Model): id = Column(Integer, Sequence('ab_role_id_seq'), primary_key=True) name = Column(String(64), unique=True, nullable=False) - permissions = relationship('PermissionView', secondary=assoc_permissionview_role, backref='role') + permissions = relationship( + 'PermissionView', + secondary=assoc_permissionview_role, + backref='role' + ) def __repr__(self): return self.name -assoc_user_role = Table('ab_user_role', Model.metadata, - Column('id', Integer, Sequence('ab_user_role_id_seq'), primary_key=True), - Column('user_id', Integer, ForeignKey('ab_user.id')), - Column('role_id', Integer, ForeignKey('ab_role.id')), - UniqueConstraint('user_id', 'role_id') +assoc_user_role = Table( + 'ab_user_role', Model.metadata, + Column('id', Integer, Sequence('ab_user_role_id_seq'), primary_key=True), + Column('user_id', Integer, ForeignKey('ab_user.id')), + Column('role_id', Integer, ForeignKey('ab_role.id')), + UniqueConstraint('user_id', 'role_id') ) @@ -99,10 +106,16 @@ def changed_by_fk(self): return Column(Integer, ForeignKey('ab_user.id'), default=self.get_user_id, nullable=True) - created_by = relationship("User", backref=backref("created", uselist=True), - remote_side=[id], primaryjoin='User.created_by_fk == User.id', uselist=False) - changed_by = relationship("User", backref=backref("changed", uselist=True), - remote_side=[id], primaryjoin='User.changed_by_fk == User.id', uselist=False) + created_by = relationship( + "User", + backref=backref("created", uselist=True), + remote_side=[id], primaryjoin='User.created_by_fk == User.id', uselist=False + ) + changed_by = relationship( + "User", + backref=backref("changed", uselist=True), + remote_side=[id], primaryjoin='User.changed_by_fk == User.id', uselist=False + ) @classmethod def get_user_id(cls): diff --git a/flask_appbuilder/security/views.py b/flask_appbuilder/security/views.py index 74c0ebd6c..dded810c3 100644 --- a/flask_appbuilder/security/views.py +++ b/flask_appbuilder/security/views.py @@ -1,15 +1,16 @@ import re import datetime import logging -from flask import flash, redirect, session, url_for, request, g, make_response, jsonify, abort +import jwt +from flask import flash, redirect, session, url_for, request, g, abort from werkzeug.security import generate_password_hash from wtforms import validators, PasswordField from wtforms.validators import EqualTo from flask_babel import lazy_gettext from flask_login import login_user, logout_user -import jwt - +from flask_jwt_extended import create_access_token from ..views import ModelView, SimpleFormView, expose +from ..api import BaseApi, safe from ..baseviews import BaseView from ..charts.views import DirectByChartView from ..fieldwidgets import BS3PasswordFieldWidget @@ -55,7 +56,10 @@ class PermissionViewModelView(ModelView): add_title = lazy_gettext('Add Permission on Views/Menus') edit_title = lazy_gettext('Edit Permission on Views/Menus') - label_columns = {'permission': lazy_gettext('Permission'), 'view_menu': lazy_gettext('View/Menu')} + label_columns = { + 'permission': lazy_gettext('Permission'), + 'view_menu': lazy_gettext('View/Menu') + } list_columns = ['permission', 'view_menu'] @@ -99,8 +103,9 @@ class UserInfoEditView(SimpleFormView): def form_get(self, form): item = self.appbuilder.sm.get_user_by_id(g.user.id) # fills the form generic solution - for key, value in form.data.items(): - if key == 'csrf_token': continue + for key, value in form.data.items(): + if key == 'csrf_token': + continue form_field = getattr(form, key) form_field.data = getattr(item, key) @@ -120,35 +125,43 @@ class UserModelView(ModelView): add_title = lazy_gettext('Add User') edit_title = lazy_gettext('Edit User') - label_columns = {'get_full_name': lazy_gettext('Full Name'), - 'first_name': lazy_gettext('First Name'), - 'last_name': lazy_gettext('Last Name'), - 'username': lazy_gettext('User Name'), - 'password': lazy_gettext('Password'), - 'active': lazy_gettext('Is Active?'), - 'email': lazy_gettext('Email'), - 'roles': lazy_gettext('Role'), - 'last_login': lazy_gettext('Last login'), - 'login_count': lazy_gettext('Login count'), - 'fail_login_count': lazy_gettext('Failed login count'), - 'created_on': lazy_gettext('Created on'), - 'created_by': lazy_gettext('Created by'), - 'changed_on': lazy_gettext('Changed on'), - 'changed_by': lazy_gettext('Changed by')} - - description_columns = {'first_name': lazy_gettext('Write the user first name or names'), - 'last_name': lazy_gettext('Write the user last name'), - 'username': lazy_gettext( - 'Username valid for authentication on DB or LDAP, unused for OID auth'), - 'password': lazy_gettext( - 'Please use a good password policy, this application does not check this for you'), - 'active': lazy_gettext('It\'s not a good policy to remove a user, just make it inactive'), - 'email': lazy_gettext('The user\'s email, this will also be used for OID auth'), - 'roles': lazy_gettext( - 'The user role on the application, this will associate with a list of permissions'), - 'conf_password': lazy_gettext('Please rewrite the user\'s password to confirm')} - - list_columns = ['first_name', 'last_name', 'username', 'email', 'active', 'roles'] + label_columns = { + 'get_full_name': lazy_gettext('Full Name'), + 'first_name': lazy_gettext('First Name'), + 'last_name': lazy_gettext('Last Name'), + 'username': lazy_gettext('User Name'), + 'password': lazy_gettext('Password'), + 'active': lazy_gettext('Is Active?'), + 'email': lazy_gettext('Email'), + 'roles': lazy_gettext('Role'), + 'last_login': lazy_gettext('Last login'), + 'login_count': lazy_gettext('Login count'), + 'fail_login_count': lazy_gettext('Failed login count'), + 'created_on': lazy_gettext('Created on'), + 'created_by': lazy_gettext('Created by'), + 'changed_on': lazy_gettext('Changed on'), + 'changed_by': lazy_gettext('Changed by') + } + + description_columns = { + 'first_name': lazy_gettext('Write the user first name or names'), + 'last_name': lazy_gettext('Write the user last name'), + 'username': lazy_gettext('Username valid for authentication on DB or LDAP, unused for OID auth'), + 'password': lazy_gettext('Please use a good password policy, this application does not check this for you'), + 'active': lazy_gettext('It\'s not a good policy to remove a user, just make it inactive'), + 'email': lazy_gettext('The user\'s email, this will also be used for OID auth'), + 'roles': lazy_gettext('The user role on the application, this will associate with a list of permissions'), + 'conf_password': lazy_gettext('Please rewrite the user\'s password to confirm') + } + + list_columns = [ + 'first_name', + 'last_name', + 'username', + 'email', + 'active', + 'roles' + ] show_fieldsets = [ (lazy_gettext('User info'), @@ -169,22 +182,46 @@ class UserModelView(ModelView): search_exclude_columns = ['password'] - add_columns = ['first_name', 'last_name', 'username', 'active', 'email', 'roles'] - edit_columns = ['first_name', 'last_name', 'username', 'active', 'email', 'roles'] + add_columns = [ + 'first_name', + 'last_name', + 'username', + 'active', + 'email', + 'roles'] + edit_columns = [ + 'first_name', + 'last_name', + 'username', + 'active', + 'email', + 'roles' + ] user_info_title = lazy_gettext("Your user information") @expose('/userinfo/') @has_access def userinfo(self): item = self.datamodel.get(g.user.id, self._base_filters) - widgets = self._get_show_widget(g.user.id, item, show_fieldsets=self.user_show_fieldsets) + widgets = self._get_show_widget( + g.user.id, + item, + show_fieldsets=self.user_show_fieldsets + ) self.update_redirect() - return self.render_template(self.show_template, - title=self.user_info_title, - widgets=widgets, - appbuilder=self.appbuilder) + return self.render_template( + self.show_template, + title=self.user_info_title, + widgets=widgets, + appbuilder=self.appbuilder + ) - @action('userinfoedit', lazy_gettext("Edit User"), "", "fa-edit", multiple=False) + @action( + 'userinfoedit', + lazy_gettext("Edit User"), + "", + "fa-edit", + multiple=False) def userinfoedit(self, item): return redirect(url_for(self.appbuilder.sm.userinfoeditview.__name__ + '.this_form_get')) @@ -231,60 +268,101 @@ class UserDBModelView(UserModelView): Override to implement your own custom view. Then override userdbmodelview property on SecurityManager """ - add_form_extra_fields = {'password': PasswordField(lazy_gettext('Password'), - description=lazy_gettext( - 'Please use a good password policy, this application does not check this for you'), - validators=[validators.DataRequired()], - widget=BS3PasswordFieldWidget()), - 'conf_password': PasswordField(lazy_gettext('Confirm Password'), - description=lazy_gettext( - 'Please rewrite the user\'s password to confirm'), - validators=[EqualTo('password', message=lazy_gettext( - 'Passwords must match'))], - widget=BS3PasswordFieldWidget())} - - add_columns = ['first_name', 'last_name', 'username', 'active', 'email', 'roles', 'password', 'conf_password'] + add_form_extra_fields = { + 'password': PasswordField( + lazy_gettext('Password'), + description=lazy_gettext('Please use a good password policy, this application does not check this for you'), + validators=[validators.DataRequired()], + widget=BS3PasswordFieldWidget() + ), + 'conf_password': PasswordField( + lazy_gettext('Confirm Password'), + description=lazy_gettext('Please rewrite the user\'s password to confirm'), + validators=[EqualTo('password', message=lazy_gettext('Passwords must match'))], + widget=BS3PasswordFieldWidget() + ) + } + + add_columns = [ + 'first_name', + 'last_name', + 'username', + 'active', + 'email', + 'roles', + 'password', + 'conf_password' + ] @expose('/show/', methods=['GET']) @has_access def show(self, pk): - actions = {} + actions = dict() actions['resetpasswords'] = self.actions.get('resetpasswords') item = self.datamodel.get(pk, self._base_filters) if not item: abort(404) widgets = self._get_show_widget(pk, item, actions=actions) self.update_redirect() - return self.render_template(self.show_template, - pk=pk, - title=self.show_title, - widgets=widgets, - appbuilder=self.appbuilder, - related_views=self._related_views) + return self.render_template( + self.show_template, + pk=pk, + title=self.show_title, + widgets=widgets, + appbuilder=self.appbuilder, + related_views=self._related_views + ) @expose('/userinfo/') @has_access def userinfo(self): - actions = {} + actions = dict() actions['resetmypassword'] = self.actions.get('resetmypassword') actions['userinfoedit'] = self.actions.get('userinfoedit') item = self.datamodel.get(g.user.id, self._base_filters) - widgets = self._get_show_widget(g.user.id, item, actions=actions, show_fieldsets=self.user_show_fieldsets) + widgets = self._get_show_widget( + g.user.id, + item, + actions=actions, + show_fieldsets=self.user_show_fieldsets + ) self.update_redirect() - return self.render_template(self.show_template, - title=self.user_info_title, - widgets=widgets, - appbuilder=self.appbuilder, + return self.render_template( + self.show_template, + title=self.user_info_title, + widgets=widgets, + appbuilder=self.appbuilder, ) - @action('resetmypassword', lazy_gettext("Reset my password"), "", "fa-lock", multiple=False) + @action( + 'resetmypassword', + lazy_gettext("Reset my password"), + "", + "fa-lock", + multiple=False + ) def resetmypassword(self, item): - return redirect(url_for(self.appbuilder.sm.resetmypasswordview.__name__ + '.this_form_get')) + return redirect( + url_for( + self.appbuilder.sm.resetmypasswordview.__name__ + '.this_form_get' + ) + ) - @action('resetpasswords', lazy_gettext("Reset Password"), "", "fa-lock", multiple=False) + @action( + 'resetpasswords', + lazy_gettext("Reset Password"), + "", + "fa-lock", + multiple=False + ) def resetpasswords(self, item): - return redirect(url_for(self.appbuilder.sm.resetpasswordview.__name__ + '.this_form_get', pk=item.id)) + return redirect( + url_for( + self.appbuilder.sm.resetpasswordview.__name__ + + '.this_form_get', pk=item.id + ) + ) def pre_update(self, item): item.changed_on = datetime.datetime.now() @@ -296,9 +374,10 @@ def pre_add(self, item): class UserStatsChartView(DirectByChartView): chart_title = lazy_gettext('User Statistics') - label_columns = {'username': lazy_gettext('User Name'), - 'login_count': lazy_gettext('Login count'), - 'fail_login_count': lazy_gettext('Failed login count') + label_columns = { + 'username': lazy_gettext('User Name'), + 'login_count': lazy_gettext('Login count'), + 'fail_login_count': lazy_gettext('Failed login count') } search_columns = UserModelView.search_columns @@ -326,11 +405,20 @@ class RoleModelView(ModelView): add_title = lazy_gettext('Add Role') edit_title = lazy_gettext('Edit Role') - label_columns = {'name': lazy_gettext('Name'), 'permissions': lazy_gettext('Permissions')} + label_columns = { + 'name': lazy_gettext('Name'), + 'permissions': lazy_gettext('Permissions') + } list_columns = ['name', 'permissions'] order_columns = ['name'] - @action("copyrole", lazy_gettext('Copy Role'), lazy_gettext('Copy the selected roles?'), icon='fa-copy', single=False) + @action( + "copyrole", + lazy_gettext('Copy Role'), + lazy_gettext('Copy the selected roles?'), + icon='fa-copy', + single=False + ) def copy_role(self, items): self.update_redirect() for item in items: @@ -347,7 +435,7 @@ class RegisterUserModelView(ModelView): base_permissions = ['can_list', 'can_show', 'can_delete'] list_title = lazy_gettext('List of Registration Requests') show_title = lazy_gettext('Show Registration') - list_columns = ['username','registration_date','email'] + list_columns = ['username', 'registration_date', 'email'] show_exclude_columns = ['password'] search_exclude_columns = ['password'] @@ -355,9 +443,7 @@ class RegisterUserModelView(ModelView): class AuthView(BaseView): route_base = '' login_template = '' - invalid_login_message = lazy_gettext('Invalid login. Please try again.') - title = lazy_gettext('Sign In') @expose('/login/', methods=['GET', 'POST']) @@ -379,16 +465,21 @@ def login(self): return redirect(self.appbuilder.get_url_for_index) form = LoginForm_db() if form.validate_on_submit(): - user = self.appbuilder.sm.auth_user_db(form.username.data, form.password.data) + user = self.appbuilder.sm.auth_user_db( + form.username.data, + form.password.data + ) if not user: flash(as_unicode(self.invalid_login_message), 'warning') return redirect(self.appbuilder.get_url_for_login) login_user(user, remember=False) return redirect(self.appbuilder.get_url_for_index) - return self.render_template(self.login_template, - title=self.title, - form=form, - appbuilder=self.appbuilder) + return self.render_template( + self.login_template, + title=self.title, + form=form, + appbuilder=self.appbuilder + ) class AuthLDAPView(AuthView): @@ -400,16 +491,21 @@ def login(self): return redirect(self.appbuilder.get_url_for_index) form = LoginForm_db() if form.validate_on_submit(): - user = self.appbuilder.sm.auth_user_ldap(form.username.data, form.password.data) + user = self.appbuilder.sm.auth_user_ldap( + form.username.data, + form.password.data + ) if not user: flash(as_unicode(self.invalid_login_message), 'warning') return redirect(self.appbuilder.get_url_for_login) login_user(user, remember=False) return redirect(self.appbuilder.get_url_for_index) - return self.render_template(self.login_template, - title=self.title, - form=form, - appbuilder=self.appbuilder) + return self.render_template( + self.login_template, + title=self.title, + form=form, + appbuilder=self.appbuilder + ) """ For Future Use, API Auth, must check howto keep REST stateless @@ -456,13 +552,17 @@ def login_handler(self): form = LoginForm_oid() if form.validate_on_submit(): session['remember_me'] = form.remember_me.data - return self.appbuilder.sm.oid.try_login(form.openid.data, ask_for=self.oid_ask_for, - ask_for_optional=self.oid_ask_for_optional) - return self.render_template(self.login_template, - title=self.title, - form=form, - providers=self.appbuilder.sm.openid_providers, - appbuilder=self.appbuilder + return self.appbuilder.sm.oid.try_login( + form.openid.data, + ask_for=self.oid_ask_for, + ask_for_optional=self.oid_ask_for_optional + ) + return self.render_template( + self.login_template, + title=self.title, + form=form, + providers=self.appbuilder.sm.openid_providers, + appbuilder=self.appbuilder ) @self.appbuilder.sm.oid.after_login @@ -488,7 +588,6 @@ def after_login(resp): class AuthOAuthView(AuthView): login_template = 'appbuilder/general/security/login_oauth.html' - @expose('/login/') @expose('/login/') @expose('/login//') @@ -498,10 +597,12 @@ def login(self, provider=None, register=None): log.debug("Already authenticated {0}".format(g.user)) return redirect(self.appbuilder.get_url_for_index) if provider is None: - return self.render_template(self.login_template, - providers = self.appbuilder.sm.oauth_providers, - title=self.title, - appbuilder=self.appbuilder) + return self.render_template( + self.login_template, + providers=self.appbuilder.sm.oauth_providers, + title=self.title, + appbuilder=self.appbuilder + ) else: log.debug("Going to call authorize for: {0}".format(provider)) state = jwt.encode( diff --git a/flask_appbuilder/templates/appbuilder/swagger/swagger.html b/flask_appbuilder/templates/appbuilder/swagger/swagger.html new file mode 100644 index 000000000..79559170b --- /dev/null +++ b/flask_appbuilder/templates/appbuilder/swagger/swagger.html @@ -0,0 +1,27 @@ +{% extends "appbuilder/base.html" %} + +{% block head_css %} +{{ super() }} + + +{% endblock %} + +{% block content %} +
+
+ + + +{% endblock %} diff --git a/flask_appbuilder/tests/test_ldapsearch.py b/flask_appbuilder/tests/_test_ldapsearch.py similarity index 100% rename from flask_appbuilder/tests/test_ldapsearch.py rename to flask_appbuilder/tests/_test_ldapsearch.py diff --git a/flask_appbuilder/tests/sqla/__init__.py b/flask_appbuilder/tests/sqla/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/flask_appbuilder/tests/sqla/models.py b/flask_appbuilder/tests/sqla/models.py new file mode 100644 index 000000000..30468d4d7 --- /dev/null +++ b/flask_appbuilder/tests/sqla/models.py @@ -0,0 +1,137 @@ +import enum +from sqlalchemy import Column, Integer, String, \ + ForeignKey, Date, Float, Enum, DateTime, Table, UniqueConstraint +from marshmallow import ValidationError +from sqlalchemy.orm import relationship +from flask_appbuilder import Model, SQLA + + +def validate_name(n): + if n[0] != 'A': + raise ValidationError('Name must start with an A') + + +class Model1(Model): + id = Column(Integer, primary_key=True) + field_string = Column(String(50), unique=True, nullable=False) + field_integer = Column(Integer()) + field_float = Column(Float()) + field_date = Column(Date()) + + def __repr__(self): + return str(self.field_string) + + def full_concat(self): + return "{}.{}.{}.{}".format( + self.field_string, + self.field_integer, + self.field_float, + self.field_date + ) + + +class Model2(Model): + id = Column(Integer, primary_key=True) + field_string = Column(String(50), unique=True, nullable=False) + field_integer = Column(Integer()) + field_float = Column(Float()) + field_date = Column(Date()) + excluded_string = Column(String(50), default='EXCLUDED') + default_string = Column(String(50), default='DEFAULT') + group_id = Column(Integer, ForeignKey('model1.id'), nullable=False) + group = relationship("Model1") + + def __repr__(self): + return str(self.field_string) + + def field_method(self): + return "field_method_value" + + +class Model3(Model): + pk1 = Column(Integer(), primary_key=True) + pk2 = Column(DateTime(), primary_key=True) + field_string = Column(String(50), unique=True, nullable=False) + + def __repr__(self): + return str(self.field_string) + + +class TmpEnum(enum.Enum): + e1 = 'a' + e2 = 2 + + +class ModelWithEnums(Model): + id = Column(Integer, primary_key=True) + enum1 = Column(Enum('e1', 'e2')) + enum2 = Column(Enum(TmpEnum), info={'enum_class': TmpEnum}) + + +assoc_parent_child = Table( + 'parent_child', Model.metadata, + Column('id', Integer, primary_key=True), + Column('parent_id', Integer, ForeignKey('parent.id')), + Column('child_id', Integer, ForeignKey('child.id')), + UniqueConstraint('parent_id', 'child_id') +) + + +class ModelMMParent(Model): + __tablename__ = 'parent' + id = Column(Integer, primary_key=True) + field_string = Column(String(50), unique=True, nullable=False) + children = relationship('ModelMMChild', secondary=assoc_parent_child) + + +class ModelMMChild(Model): + __tablename__ = 'child' + id = Column(Integer, primary_key=True) + field_string = Column(String(50), unique=True, nullable=False) + + + """ --------------------------------- + TEST HELPER FUNCTIONS + --------------------------------- + """ + + +def insert_data(session, count): + model1_collection = list() + for i in range(count): + model = Model1() + model.field_string = "test{}".format(i) + model.field_integer = i + model.field_float = float(i) + session.add(model) + session.commit() + model1_collection.append(model) + for i in range(count): + model = Model2() + model.field_string = "test{}".format(i) + model.field_integer = i + model.field_float = float(i) + model.group = model1_collection[i] + session.add(model) + session.commit() + for i in range(count): + model = ModelWithEnums() + model.enum1 = 'e1' + model.enum2 = TmpEnum.e2 + session.add(model) + session.commit() + + children = list() + for i in range(1, 4): + model = ModelMMChild() + model.field_string = str(i) + children.append(model) + session.add(model) + session.commit() + for i in range(count): + model = ModelMMParent() + model.field_string = str(i) + model.children = children + session.add(model) + session.commit() + diff --git a/flask_appbuilder/tests/test_api.py b/flask_appbuilder/tests/test_api.py new file mode 100644 index 000000000..475a39629 --- /dev/null +++ b/flask_appbuilder/tests/test_api.py @@ -0,0 +1,1736 @@ +import unittest +import os +import json +import logging +import prison +from nose.tools import eq_ +from flask_appbuilder import SQLA +from .sqla.models import Model1, Model2, ModelWithEnums, TmpEnum, \ + ModelMMParent, ModelMMChild, insert_data, validate_name +from flask_appbuilder.models.sqla.filters import \ + FilterGreater, FilterSmaller +from flask_appbuilder.const import ( + API_URI_RIS_KEY, + API_ORDER_COLUMNS_RES_KEY, + API_LABEL_COLUMNS_RES_KEY, + API_LIST_COLUMNS_RES_KEY, + API_DESCRIPTION_COLUMNS_RES_KEY, + API_SHOW_COLUMNS_RES_KEY, + API_ADD_COLUMNS_RES_KEY, + API_EDIT_COLUMNS_RES_KEY, + API_FILTERS_RES_KEY, + API_PERMISSIONS_RES_KEY, + API_RESULT_RES_KEY, + API_ORDER_COLUMNS_RIS_KEY, + API_LABEL_COLUMNS_RIS_KEY, + API_LIST_COLUMNS_RIS_KEY, + API_DESCRIPTION_COLUMNS_RIS_KEY, + API_SHOW_COLUMNS_RIS_KEY, + API_ADD_COLUMNS_RIS_KEY, + API_EDIT_COLUMNS_RIS_KEY, + API_SELECT_COLUMNS_RIS_KEY, + API_SELECT_KEYS_RIS_KEY, + API_FILTERS_RIS_KEY, + API_PERMISSIONS_RIS_KEY, + API_SECURITY_USERNAME_KEY, + API_SECURITY_PASSWORD_KEY, + API_SECURITY_PROVIDER_KEY, + API_SECURITY_ACCESS_TOKEN_KEY, + API_SECURITY_REFRESH_TOKEN_KEY, + API_SECURITY_VERSION, + API_LIST_TITLE_RIS_KEY, + API_LIST_TITLE_RES_KEY, + API_SHOW_TITLE_RIS_KEY, + API_SHOW_TITLE_RES_KEY, + API_ADD_TITLE_RIS_KEY, + API_ADD_TITLE_RES_KEY, + API_EDIT_TITLE_RIS_KEY, + API_EDIT_TITLE_RES_KEY +) + + +log = logging.getLogger(__name__) + +MODEL1_DATA_SIZE = 30 +MODEL2_DATA_SIZE = 30 +USERNAME = "testadmin" +PASSWORD = "password" +MAX_PAGE_SIZE = 25 + + +class FlaskTestCase(unittest.TestCase): + + def setUp(self): + from flask import Flask + from flask_appbuilder import AppBuilder + from flask_appbuilder.models.sqla.interface import SQLAInterface + from flask_appbuilder import ModelRestApi + + self.app = Flask(__name__) + self.basedir = os.path.abspath(os.path.dirname(__file__)) + self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + self.app.config['SECRET_KEY'] = 'thisismyscretkey' + self.app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + self.app.config['FAB_API_MAX_PAGE_SIZE'] = MAX_PAGE_SIZE + self.app.config['WTF_CSRF_ENABLED'] = False + + self.db = SQLA(self.app) + self.appbuilder = AppBuilder(self.app, self.db.session) + # Create models and insert data + insert_data(self.db.session, MODEL1_DATA_SIZE) + + class Model1Api(ModelRestApi): + datamodel = SQLAInterface(Model1) + list_columns = [ + 'field_integer', + 'field_float', + 'field_string', + 'field_date' + ] + description_columns = { + 'field_integer': 'Field Integer', + 'field_float': 'Field Float', + 'field_string': 'Field String' + } + + self.model1api = Model1Api + self.appbuilder.add_api(Model1Api) + + class Model1ApiFieldsInfo(Model1Api): + datamodel = SQLAInterface(Model1) + add_columns = [ + 'field_integer', + 'field_float', + 'field_string', + 'field_date' + ] + edit_columns = [ + 'field_string', + 'field_integer' + ] + + self.model1apifieldsinfo = Model1ApiFieldsInfo + self.appbuilder.add_api(Model1ApiFieldsInfo) + + class Model1FuncApi(ModelRestApi): + datamodel = SQLAInterface(Model1) + list_columns = [ + 'field_integer', + 'field_float', + 'field_string', + 'field_date', + 'full_concat' + ] + description_columns = { + 'field_integer': 'Field Integer', + 'field_float': 'Field Float', + 'field_string': 'Field String' + } + + self.model1funcapi = Model1Api + self.appbuilder.add_api(Model1FuncApi) + + class Model1ApiExcludeCols(ModelRestApi): + datamodel = SQLAInterface(Model1) + list_exclude_columns = [ + 'field_integer', + 'field_float', + 'field_date' + ] + show_exclude_columns = list_exclude_columns + edit_exclude_columns = list_exclude_columns + add_exclude_columns = list_exclude_columns + + self.appbuilder.add_api(Model1ApiExcludeCols) + + class Model1ApiOrder(ModelRestApi): + datamodel = SQLAInterface(Model1) + base_order = ('field_integer', 'desc') + + self.appbuilder.add_api(Model1ApiOrder) + + class Model1ApiRestrictedPermissions(ModelRestApi): + datamodel = SQLAInterface(Model1) + base_permissions = ['can_get', 'can_info'] + + self.appbuilder.add_api(Model1ApiRestrictedPermissions) + + class Model1ApiFiltered(ModelRestApi): + datamodel = SQLAInterface(Model1) + base_filters = [ + ['field_integer', FilterGreater, 2], + ['field_integer', FilterSmaller, 4] + ] + + self.appbuilder.add_api(Model1ApiFiltered) + + class ModelWithEnumsApi(ModelRestApi): + datamodel = SQLAInterface(ModelWithEnums) + + self.appbuilder.add_api(ModelWithEnumsApi) + + class Model1BrowserLogin(ModelRestApi): + datamodel = SQLAInterface(Model1) + allow_browser_login = True + + self.appbuilder.add_api(Model1BrowserLogin) + + class ModelMMApi(ModelRestApi): + datamodel = SQLAInterface(ModelMMParent) + + self.appbuilder.add_api(ModelMMApi) + + class Model1CustomValidationApi(ModelRestApi): + datamodel = SQLAInterface(Model1) + validators_columns = { + "field_string": validate_name + } + self.appbuilder.add_api(Model1CustomValidationApi) + + class Model2Api(ModelRestApi): + datamodel = SQLAInterface(Model2) + list_columns = [ + 'group' + ] + show_columns = [ + 'group' + ] + + self.model2api = Model2Api + self.appbuilder.add_api(Model2Api) + + class Model2ApiFilteredRelFields(ModelRestApi): + datamodel = SQLAInterface(Model2) + list_columns = [ + 'group' + ] + show_columns = [ + 'group' + ] + add_query_rel_fields = { + 'group': [ + ['field_integer', FilterGreater, 2], + ['field_integer', FilterSmaller, 4] + ] + } + edit_query_rel_fields = add_query_rel_fields + + self.model2apifilteredrelfields = Model2ApiFilteredRelFields + self.appbuilder.add_api(Model2ApiFilteredRelFields) + + role_admin = self.appbuilder.sm.find_role('Admin') + self.appbuilder.sm.add_user( + USERNAME, + 'admin', + 'user', + 'admin@fab.org', + role_admin, + PASSWORD + ) + + def tearDown(self): + self.appbuilder = None + self.app = None + self.db = None + + @staticmethod + def auth_client_get(client, token, uri): + return client.get( + uri, + headers={"Authorization": "Bearer {}".format(token)} + ) + + @staticmethod + def auth_client_delete(client, token, uri): + return client.delete( + uri, + headers={"Authorization": "Bearer {}".format(token)} + ) + + @staticmethod + def auth_client_put(client, token, uri, json): + return client.put( + uri, + json=json, + headers={"Authorization": "Bearer {}".format(token)} + ) + + @staticmethod + def auth_client_post(client, token, uri, json): + return client.post( + uri, + json=json, + headers={"Authorization": "Bearer {}".format(token)} + ) + + @staticmethod + def _login(client, username, password): + """ + Login help method + :param client: Flask test client + :param username: username + :param password: password + :return: Flask client response class + """ + return client.post( + 'api/{}/security/login'.format(API_SECURITY_VERSION), + data=json.dumps( + { + API_SECURITY_USERNAME_KEY: username, + API_SECURITY_PASSWORD_KEY: password, + API_SECURITY_PROVIDER_KEY: "db" + } + ), + content_type='application/json' + ) + + def login(self, client, username, password): + # Login with default admin + rv = self._login(client, username, password) + try: + return json.loads(rv.data.decode('utf-8')).get("access_token") + except: + return rv + + def browser_login(self, client, username, password): + # Login with default admin + rv = client.post('/login/', data=dict( + username=username, + password=password + ), follow_redirects=True) + + def browser_logout(self, client): + return client.get('/logout/') + + def test_auth_login(self): + """ + REST Api: Test auth login + """ + client = self.app.test_client() + rv = self._login(client, USERNAME, PASSWORD) + eq_(rv.status_code, 200) + assert json.loads( + rv.data.decode('utf-8') + ).get(API_SECURITY_ACCESS_TOKEN_KEY, False) + + def test_auth_login_failed(self): + """ + REST Api: Test auth login failed + """ + client = self.app.test_client() + rv = self._login(client, "fail", "fail") + eq_(json.loads(rv.data), {"message": "Not authorized"}) + eq_(rv.status_code, 401) + + def test_auth_login_bad(self): + """ + REST Api: Test auth login bad request + """ + client = self.app.test_client() + rv = client.post( + 'api/v1/security/login', + data="BADADATA" + ) + eq_(rv.status_code, 400) + + def test_auth_authorization_browser(self): + """ + REST Api: Test auth with browser login + """ + client = self.app.test_client() + rv = self.browser_login(client, USERNAME, PASSWORD) + # Test access with browser login + uri = 'api/v1/model1browserlogin/1' + rv = client.get( + uri + ) + eq_(rv.status_code, 200) + # Test unauthorized access with browser login + uri = 'api/v1/model1api/1' + rv = client.get( + uri + ) + eq_(rv.status_code, 401) + # Test access wihout cookie or JWT + rv = self.browser_logout(client) + # Test access with browser login + uri = 'api/v1/model1browserlogin/1' + rv = client.get( + uri + ) + eq_(rv.status_code, 401) + # Test access with JWT but without cookie + token = self.login(client, USERNAME, PASSWORD) + uri = 'api/v1/model1browserlogin/1' + rv = self.auth_client_get( + client, + token, + uri + ) + eq_(rv.status_code, 200) + + def test_auth_authorization(self): + """ + REST Api: Test auth base limited authorization + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + # Test unauthorized DELETE + pk = 1 + uri = 'api/v1/model1apirestrictedpermissions/{}'.format(pk) + rv = self.auth_client_delete( + client, + token, + uri + ) + eq_(rv.status_code, 401) + # Test unauthorized POST + item = dict( + field_string="test{}".format(MODEL1_DATA_SIZE+1), + field_integer=MODEL1_DATA_SIZE+1, + field_float=float(MODEL1_DATA_SIZE+1), + field_date=None + ) + uri = 'api/v1/model1apirestrictedpermissions/' + rv = self.auth_client_post( + client, + token, + uri, + item + ) + eq_(rv.status_code, 401) + # Test unauthorized GET + uri = 'api/v1/model1apirestrictedpermissions/1' + rv = self.auth_client_get( + client, + token, + uri + ) + eq_(rv.status_code, 200) + + def test_get_item(self): + """ + REST Api: Test get item + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + for i in range(1, MODEL1_DATA_SIZE): + rv = self.auth_client_get( + client, + token, + 'api/v1/model1api/{}'.format(i) + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(rv.status_code, 200) + self.assert_get_item(rv, data, i - 1) + + def assert_get_item(self, rv, data, value): + eq_(data[API_RESULT_RES_KEY], { + 'field_date': None, + 'field_float': float(value), + 'field_integer': value, + 'field_string': "test{}".format(value) + }) + # test descriptions + eq_(data['description_columns'], self.model1api.description_columns) + # test labels + eq_(data[API_LABEL_COLUMNS_RES_KEY], { + 'field_date': 'Field Date', + 'field_float': 'Field Float', + 'field_integer': 'Field Integer', + 'field_string': 'Field String' + }) + eq_(rv.status_code, 200) + + def test_get_item_select_cols(self): + """ + REST Api: Test get item with select columns + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + for i in range(1, MODEL1_DATA_SIZE): + uri = ('api/v1/model1api/{}?q=({}:!(field_integer))' + .format(i, API_SELECT_COLUMNS_RIS_KEY)) + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY], {'field_integer': i - 1}) + eq_(data[API_DESCRIPTION_COLUMNS_RES_KEY], { + 'field_integer': 'Field Integer' + }) + eq_(data[API_LABEL_COLUMNS_RES_KEY], { + 'field_integer': 'Field Integer' + }) + eq_(rv.status_code, 200) + + def test_get_item_select_meta_data(self): + """ + REST Api: Test get item select meta data + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + selectable_keys = [ + API_DESCRIPTION_COLUMNS_RIS_KEY, + API_LABEL_COLUMNS_RIS_KEY, + API_SHOW_COLUMNS_RIS_KEY, + API_SHOW_TITLE_RIS_KEY + ] + for selectable_key in selectable_keys: + argument = { + API_SELECT_KEYS_RIS_KEY: [ + selectable_key + ] + } + uri = 'api/v1/model1api/1?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(argument) + ) + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(len(data.keys()), 1 + 2) # always exist id, result + # We assume that rison meta key equals result meta key + assert selectable_key in data + + def test_get_item_excluded_cols(self): + """ + REST Api: Test get item with excluded columns + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + pk = 1 + rv = self.auth_client_get( + client, + token, + 'api/v1/model1apiexcludecols/{}'.format(pk) + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY], { + 'field_string': 'test0' + }) + eq_(rv.status_code, 200) + + def test_get_item_not_found(self): + """ + REST Api: Test get item not found + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + pk = MODEL1_DATA_SIZE + 1 + rv = self.auth_client_get( + client, + token, + 'api/v1/model1api/{}'.format(pk) + ) + eq_(rv.status_code, 404) + + def test_get_item_base_filters(self): + """ + REST Api: Test get item with base filters + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + # We can't get a base filtered item + pk = 1 + rv = self.auth_client_get( + client, + token, + 'api/v1/model1apifiltered/{}'.format(pk) + ) + eq_(rv.status_code, 404) + # This one is ok pk=4 field_integer=3 2>3<4 + pk = 4 + rv = self.auth_client_get( + client, + token, + 'api/v1/model1apifiltered/{}'.format(pk) + ) + eq_(rv.status_code, 200) + + def test_get_item_1m_field(self): + """ + REST Api: Test get item with 1-N related field + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + # We can't get a base filtered item + pk = 1 + rv = self.auth_client_get( + client, + token, + 'api/v1/model2api/{}'.format(pk) + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(rv.status_code, 200) + expected_rel_field = { + 'group': + { + 'field_date': None, + 'field_float': 0.0, + 'field_integer': 0, + 'field_string': 'test0', + 'id': 1 + } + } + eq_(data[API_RESULT_RES_KEY], expected_rel_field) + + def test_get_item_mm_field(self): + """ + REST Api: Test get item with N-N related field + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + # We can't get a base filtered item + pk = 1 + rv = self.auth_client_get( + client, + token, + 'api/v1/modelmmapi/{}'.format(pk) + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(rv.status_code, 200) + expected_rel_field = [ + {'field_string': '1', 'id': 1}, + {'field_string': '2', 'id': 2}, + {'field_string': '3', 'id': 3} + ] + eq_(data[API_RESULT_RES_KEY]['children'], expected_rel_field) + + def test_get_list(self): + """ + REST Api: Test get list + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + rv = self.auth_client_get( + client, + token, + 'api/v1/model1api/' + ) + + data = json.loads(rv.data.decode('utf-8')) + # Tests count property + eq_(data['count'], MODEL1_DATA_SIZE) + # Tests data result default page size + eq_(len(data[API_RESULT_RES_KEY]), self.model1api.page_size) + for i in range(1, self.model1api.page_size): + self.assert_get_list(rv, data[API_RESULT_RES_KEY][i - 1], i - 1) + + @staticmethod + def assert_get_list(rv, data, value): + eq_(data, { + 'field_date': None, + 'field_float': float(value), + 'field_integer': value, + 'field_string': "test{}".format(value) + }) + eq_(rv.status_code, 200) + + def test_get_list_order(self): + """ + REST Api: Test get list order params + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + # test string order asc + arguments = { + "order_column": "field_integer", + "order_direction": "asc" + } + uri = 'api/v1/model1api/?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(arguments) + ) + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY][0], { + 'field_date': None, + 'field_float': 0.0, + 'field_integer': 0, + 'field_string': "test0" + }) + eq_(rv.status_code, 200) + # test string order desc + arguments = { + "order_column": "field_integer", + "order_direction": "desc" + } + uri = 'api/v1/model1api/?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(arguments) + ) + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY][0], { + 'field_date': None, + 'field_float': float(MODEL1_DATA_SIZE - 1), + 'field_integer': MODEL1_DATA_SIZE - 1, + 'field_string': "test{}".format(MODEL1_DATA_SIZE - 1) + }) + eq_(rv.status_code, 200) + + def test_get_list_base_order(self): + """ + REST Api: Test get list with base order + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + # test string order asc + rv = self.auth_client_get( + client, + token, + 'api/v1/model1apiorder/' + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY][0], { + 'field_date': None, + 'field_float': float(MODEL1_DATA_SIZE - 1), + 'field_integer': MODEL1_DATA_SIZE - 1, + 'field_string': "test{}".format(MODEL1_DATA_SIZE - 1) + }) + # Test override + arguments = { + "order_column": "field_integer", + "order_direction": "asc" + } + uri = 'api/v1/model1apiorder/?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(arguments) + ) + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY][0], { + 'field_date': None, + 'field_float': 0.0, + 'field_integer': 0, + 'field_string': "test0" + }) + + def test_get_list_page(self): + """ + REST Api: Test get list page params + """ + page_size = 5 + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + # test page zero + arguments = { + "page_size": page_size, + "page": 0, + "order_column": "field_integer", + "order_direction": "asc" + } + uri = 'api/v1/model1api/?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(arguments) + ) + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY][0], { + 'field_date': None, + 'field_float': 0.0, + 'field_integer': 0, + 'field_string': "test0" + }) + eq_(rv.status_code, 200) + eq_(len(data[API_RESULT_RES_KEY]), page_size) + # test page one + arguments = { + "page_size": page_size, + "page": 1, + "order_column": "field_integer", + "order_direction": "asc" + } + uri = 'api/v1/model1api/?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(arguments) + ) + rv = self.auth_client_get( + client, + token, + uri + ) + + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY][0], { + 'field_date': None, + 'field_float': float(page_size), + 'field_integer': page_size, + 'field_string': "test{}".format(page_size) + }) + eq_(rv.status_code, 200) + eq_(len(data[API_RESULT_RES_KEY]), page_size) + + def test_get_list_max_page_size(self): + """ + REST Api: Test get list max page size config setting + """ + page_size = 100 # Max is globally set to MAX_PAGE_SIZE + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + # test page zero + arguments = { + "page_size": page_size, + "page": 0, + "order_column": "field_integer", + "order_direction": "asc" + } + uri = 'api/v1/model1api/?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(arguments) + ) + print("URI {}".format(uri)) + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(len(data[API_RESULT_RES_KEY]), MAX_PAGE_SIZE) + + def test_get_list_filters(self): + """ + REST Api: Test get list filter params + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + filter_value = 5 + # test string order asc + arguments = { + API_FILTERS_RIS_KEY: [ + { + "col": "field_integer", + "opr": "gt", + "value": filter_value + } + ], + "order_column": "field_integer", + "order_direction": "asc" + } + + uri = 'api/v1/model1api/?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(arguments)) + + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY][0], { + 'field_date': None, + 'field_float': float(filter_value + 1), + 'field_integer': filter_value + 1, + 'field_string': "test{}".format(filter_value + 1) + }) + eq_(rv.status_code, 200) + + def test_get_list_select_cols(self): + """ + REST Api: Test get list with selected columns + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + argument = { + API_SELECT_COLUMNS_RIS_KEY: [ + "field_integer" + ], + "order_column": "field_integer", + "order_direction": "asc" + } + + uri = 'api/v1/model1api/?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(argument) + ) + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY][0], { + 'field_integer': 0, + }) + eq_(data[API_LABEL_COLUMNS_RES_KEY], { + 'field_integer': 'Field Integer' + }) + eq_(data[API_DESCRIPTION_COLUMNS_RES_KEY], { + 'field_integer': 'Field Integer' + }) + eq_(data[API_LIST_COLUMNS_RES_KEY], [ + 'field_integer' + ]) + eq_(rv.status_code, 200) + + def test_get_list_select_meta_data(self): + """ + REST Api: Test get list select meta data + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + selectable_keys = [ + API_DESCRIPTION_COLUMNS_RIS_KEY, + API_LABEL_COLUMNS_RIS_KEY, + API_ORDER_COLUMNS_RIS_KEY, + API_LIST_COLUMNS_RIS_KEY, + API_LIST_TITLE_RIS_KEY + ] + for selectable_key in selectable_keys: + argument = { + API_SELECT_KEYS_RIS_KEY: [ + selectable_key + ] + } + uri = 'api/v1/model1api/?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(argument) + ) + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(len(data.keys()), 1 + 3) # always exist count, ids, result + # We assume that rison meta key equals result meta key + assert selectable_key in data + + def test_get_list_exclude_cols(self): + """ + REST Api: Test get list with excluded columns + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + uri = 'api/v1/model1apiexcludecols/' + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY][0], { + 'field_string': 'test0' + }) + + def test_get_list_base_filters(self): + """ + REST Api: Test get list with base filters + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + arguments = { + "order_column": "field_integer", + "order_direction": "desc" + } + uri = 'api/v1/model1apifiltered/?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(arguments) + ) + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + expected_result = [ + { + 'field_date': None, + 'field_float': 3.0, + 'field_integer': 3, + 'field_string': 'test3', + } + ] + eq_(data[API_RESULT_RES_KEY], expected_result) + + def test_info_filters(self): + """ + REST Api: Test info filters + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + uri = 'api/v1/model1api/_info' + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + expected_filters = { + 'field_date': [ + {'name': 'Equal to', 'operator': 'eq'}, + {'name': 'Greater than', 'operator': 'gt'}, + {'name': 'Smaller than', 'operator': 'lt'}, + {'name': 'Not Equal to', 'operator': 'neq'} + ], + 'field_float': [ + {'name': 'Equal to', 'operator': 'eq'}, + {'name': 'Greater than', 'operator': 'gt'}, + {'name': 'Smaller than', 'operator': 'lt'}, + {'name': 'Not Equal to', 'operator': 'neq'} + ], + 'field_integer': [ + {'name': 'Equal to', 'operator': 'eq'}, + {'name': 'Greater than', 'operator': 'gt'}, + {'name': 'Smaller than', 'operator': 'lt'}, + {'name': 'Not Equal to', 'operator': 'neq'} + ], + 'field_string': [ + {'name': 'Starts with', 'operator': 'sw'}, + {'name': 'Ends with', 'operator': 'ew'}, + {'name': 'Contains', 'operator': 'ct'}, + {'name': 'Equal to', 'operator': 'eq'}, + {'name': 'Not Starts with', 'operator': 'nsw'}, + {'name': 'Not Ends with', 'operator': 'new'}, + {'name': 'Not Contains', 'operator': 'nct'}, + {'name': 'Not Equal to', 'operator': 'neq'} + ] + } + eq_(data['filters'], expected_filters) + + def test_info_fields(self): + """ + REST Api: Test info fields (add, edit) + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + uri = 'api/v1/model1apifieldsinfo/_info' + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + expect_add_fields = [ + { + 'description': 'Field Integer', + 'label': 'Field Integer', + 'name': 'field_integer', + 'required': False, + 'unique': False, + 'type': 'Integer' + }, + { + 'description': 'Field Float', + 'label': 'Field Float', + 'name': 'field_float', + 'required': False, + 'unique': False, + 'type': 'Float' + }, + { + 'description': 'Field String', + 'label': 'Field String', + 'name': 'field_string', + 'required': True, + 'unique': True, + 'type': 'String', + 'validate': [''] + }, + { + 'description': '', + 'label': 'Field Date', + 'name': 'field_date', + 'required': False, + 'unique': False, + 'type': 'Date' + } + ] + expect_edit_fields = list() + for edit_col in self.model1apifieldsinfo.edit_columns: + for item in expect_add_fields: + if item['name'] == edit_col: + expect_edit_fields.append(item) + eq_(data[API_ADD_COLUMNS_RES_KEY], expect_add_fields) + eq_(data[API_EDIT_COLUMNS_RES_KEY], expect_edit_fields) + + def test_info_fields_rel_field(self): + """ + REST Api: Test info fields with related fields + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + uri = 'api/v1/model2api/_info' + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + expected_rel_add_field = { + 'count': MODEL2_DATA_SIZE, + 'description': '', + 'label': 'Group', + 'name': 'group', + 'required': True, + 'unique': False, + 'type': 'Related', + 'values': [] + } + for i in range(self.model2api.page_size): + expected_rel_add_field['values'].append( + { + 'id': i + 1, + 'value': "test{}".format(i) + } + ) + for rel_field in data[API_ADD_COLUMNS_RES_KEY]: + if rel_field['name'] == 'group': + eq_(rel_field, expected_rel_add_field) + + def test_info_fields_rel_filtered_field(self): + """ + REST Api: Test info fields with filtered + related fields + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + uri = 'api/v1/model2apifilteredrelfields/_info' + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + expected_rel_add_field = { + 'description': '', + 'label': 'Group', + 'name': 'group', + 'required': True, + 'unique': False, + 'type': 'Related', + 'count': 1, + 'values': [ + { + 'id': 4, + 'value': 'test3' + } + ] + } + for rel_field in data[API_ADD_COLUMNS_RES_KEY]: + if rel_field['name'] == 'group': + eq_(rel_field, expected_rel_add_field) + for rel_field in data[API_EDIT_COLUMNS_RES_KEY]: + if rel_field['name'] == 'group': + eq_(rel_field, expected_rel_add_field) + + def test_info_permissions(self): + """ + REST Api: Test info permissions + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + uri = 'api/v1/model1api/_info' + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + expected_permissions = [ + 'can_delete', + 'can_get', + 'can_info', + 'can_post', + 'can_put', + ] + eq_(sorted(data[API_PERMISSIONS_RES_KEY]), expected_permissions) + uri = 'api/v1/model1apirestrictedpermissions/_info' + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + expected_permissions = [ + 'can_get', + 'can_info' + ] + eq_(sorted(data[API_PERMISSIONS_RES_KEY]), expected_permissions) + + def test_info_select_meta_data(self): + """ + REST Api: Test info select meta data + """ + # select meta for add fields + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + selectable_keys = [ + API_ADD_COLUMNS_RIS_KEY, + API_EDIT_COLUMNS_RIS_KEY, + API_PERMISSIONS_RIS_KEY, + API_FILTERS_RIS_KEY, + API_ADD_TITLE_RIS_KEY, + API_EDIT_TITLE_RIS_KEY + ] + for selectable_key in selectable_keys: + arguments = { + API_SELECT_KEYS_RIS_KEY: [ + selectable_key + ] + } + uri = 'api/v1/model1api/_info?{}={}'.format( + API_URI_RIS_KEY, + prison.dumps(arguments) + ) + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(len(data.keys()), 1) + # We assume that rison meta key equals result meta key + assert selectable_key in data + + def test_delete_item(self): + """ + REST Api: Test delete item + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + pk = 2 + uri = 'api/v1/model1api/{}'.format(pk) + rv = self.auth_client_delete( + client, + token, + uri + ) + eq_(rv.status_code, 200) + model = self.db.session.query(Model1).get(pk) + eq_(model, None) + + def test_delete_item_not_found(self): + """ + REST Api: Test delete item not found + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + + pk = MODEL1_DATA_SIZE + 1 + uri = 'api/v1/model1api/{}'.format(pk) + rv = self.auth_client_delete( + client, + token, + uri + ) + eq_(rv.status_code, 404) + + def test_delete_item_base_filters(self): + """ + REST Api: Test delete item with base filters + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + # Try to delete a filtered item + pk = 1 + uri = 'api/v1/model1apifiltered/{}'.format(pk) + rv = self.auth_client_delete( + client, + token, + uri + ) + eq_(rv.status_code, 404) + + def test_update_item(self): + """ + REST Api: Test update item + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + pk = 3 + item = dict( + field_string="test_Put", + field_integer=0, + field_float=0.0 + ) + uri = 'api/v1/model1api/{}'.format(pk) + rv = self.auth_client_put( + client, + token, + uri, + item + ) + eq_(rv.status_code, 200) + model = self.db.session.query(Model1).get(pk) + eq_(model.field_string, "test_Put") + eq_(model.field_integer, 0) + eq_(model.field_float, 0.0) + + def test_update_custom_validation(self): + """ + REST Api: Test update item custom validation + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + pk = 3 + item = dict( + field_string="test_Put", + field_integer=0, + field_float=0.0 + ) + uri = 'api/v1/model1customvalidationapi/{}'.format(pk) + rv = self.auth_client_put( + client, + token, + uri, + item + ) + eq_(rv.status_code, 422) + pk = 3 + item = dict( + field_string="Atest_Put", + field_integer=0, + field_float=0.0 + ) + uri = 'api/v1/model1customvalidationapi/{}'.format(pk) + rv = self.auth_client_put( + client, + token, + uri, + item + ) + eq_(rv.status_code, 200) + + def test_update_item_base_filters(self): + """ + REST Api: Test update item with base filters + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + pk = 4 + item = dict( + field_string="test_Put", + field_integer=3, + field_float=3.0 + ) + uri = 'api/v1/model1apifiltered/{}'.format(pk) + rv = self.auth_client_put( + client, + token, + uri, + item + ) + eq_(rv.status_code, 200) + model = self.db.session.query(Model1).get(pk) + eq_(model.field_string, "test_Put") + eq_(model.field_integer, 3) + eq_(model.field_float, 3.0) + # We can't update an item that is base filtered + pk = 1 + uri = 'api/v1/model1apifiltered/{}'.format(pk) + rv = self.auth_client_put( + client, + token, + uri, + item + ) + eq_(rv.status_code, 404) + + def test_update_item_not_found(self): + """ + REST Api: Test update item not found + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + pk = MODEL1_DATA_SIZE + 1 + item = dict( + field_string="test_Put", + field_integer=0, + field_float=0.0 + ) + uri = 'api/v1/model1api/{}'.format(pk) + rv = self.auth_client_put( + client, + token, + uri, + item + ) + eq_(rv.status_code, 404) + + def test_update_val_size(self): + """ + REST Api: Test update validate size + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + pk = 1 + field_string = 'a' * 51 + item = dict( + field_string=field_string, + field_integer=11, + field_float=11.0 + ) + uri = 'api/v1/model1api/{}'.format(pk) + rv = self.auth_client_put( + client, + token, + uri, + item + ) + eq_(rv.status_code, 422) + data = json.loads(rv.data.decode('utf-8')) + eq_(data['message']['field_string'][0], 'Longer than maximum length 50.') + + def test_update_mm_field(self): + """ + REST Api: Test update m-m field + """ + model = ModelMMChild() + model.field_string = 'update_m,m' + self.appbuilder.get_session.add(model) + self.appbuilder.get_session.commit() + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + pk = 1 + item = dict( + children=[4] + ) + uri = 'api/v1/modelmmapi/{}'.format(pk) + rv = self.auth_client_put( + client, + token, + uri, + item + ) + eq_(rv.status_code, 200) + data = json.loads(rv.data.decode('utf-8')) + eq_(data[API_RESULT_RES_KEY], {"children": [4], "field_string": "0"}) + + def test_update_item_val_type(self): + """ + REST Api: Test update validate type + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + pk = 1 + item = dict( + field_string="test{}".format(MODEL1_DATA_SIZE+1), + field_integer="test{}".format(MODEL1_DATA_SIZE+1), + field_float=11.0 + ) + uri = 'api/v1/model1api/{}'.format(pk) + rv = self.auth_client_put( + client, + token, + uri, + item + ) + eq_(rv.status_code, 422) + data = json.loads(rv.data.decode('utf-8')) + eq_(data['message']['field_integer'][0], 'Not a valid integer.') + + item = dict( + field_string=11, + field_integer=11, + field_float=11.0 + ) + rv = self.auth_client_put( + client, + token, + uri, + item + ) + eq_(rv.status_code, 422) + data = json.loads(rv.data.decode('utf-8')) + eq_(data['message']['field_string'][0], 'Not a valid string.') + + def test_update_item_excluded_cols(self): + """ + REST Api: Test update item with excluded cols + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + pk = 1 + item = dict( + field_string="test_Put", + field_integer=1000 + ) + uri = 'api/v1/model1apiexcludecols/{}'.format(pk) + rv = self.auth_client_put( + client, + token, + uri, + item + ) + eq_(rv.status_code, 200) + model = self.db.session.query(Model1).get(pk) + eq_(model.field_integer, 0) + eq_(model.field_float, 0.0) + eq_(model.field_date, None) + + def test_create_item(self): + """ + REST Api: Test create item + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + item = dict( + field_string="test{}".format(MODEL1_DATA_SIZE+1), + field_integer=MODEL1_DATA_SIZE+1, + field_float=float(MODEL1_DATA_SIZE+1), + field_date=None + ) + uri = 'api/v1/model1api/' + rv = self.auth_client_post( + client, + token, + uri, + item + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(rv.status_code, 201) + eq_(data[API_RESULT_RES_KEY], item) + model = self.db.session.query(Model1).filter_by( + field_string='test{}'.format(MODEL1_DATA_SIZE+1) + ).first() + eq_(model.field_string, "test{}".format(MODEL1_DATA_SIZE+1)) + eq_(model.field_integer, MODEL1_DATA_SIZE+1) + eq_(model.field_float, float(MODEL1_DATA_SIZE+1)) + + def test_create_item_custom_validation(self): + """ + REST Api: Test create item custom validation + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + item = dict( + field_string="test{}".format(MODEL1_DATA_SIZE+1), + field_integer=MODEL1_DATA_SIZE+1, + field_float=float(MODEL1_DATA_SIZE+1), + field_date=None + ) + uri = 'api/v1/model1customvalidationapi/' + rv = self.auth_client_post( + client, + token, + uri, + item + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(rv.status_code, 422) + eq_(data, { + "message": { + "field_string": [ + "Name must start with an A" + ] + } + }) + item = dict( + field_string="A{}".format(MODEL1_DATA_SIZE+1), + field_integer=MODEL1_DATA_SIZE+1, + field_float=float(MODEL1_DATA_SIZE+1), + field_date=None + ) + uri = 'api/v1/model1customvalidationapi/' + rv = self.auth_client_post( + client, + token, + uri, + item + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(rv.status_code, 201) + + def test_create_item_val_size(self): + """ + REST Api: Test create validate size + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + field_string = 'a' * 51 + item = dict( + field_string=field_string, + field_integer=MODEL1_DATA_SIZE+1, + field_float=float(MODEL1_DATA_SIZE+1) + ) + uri = 'api/v1/model1api/' + rv = self.auth_client_post( + client, + token, + uri, + item + ) + eq_(rv.status_code, 422) + data = json.loads(rv.data.decode('utf-8')) + eq_(data['message']['field_string'][0], 'Longer than maximum length 50.') + + def test_create_item_val_type(self): + """ + REST Api: Test create validate type + """ + # Test integer as string + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + item = dict( + field_string="test{}".format(MODEL1_DATA_SIZE), + field_integer="test{}".format(MODEL1_DATA_SIZE), + field_float=float(MODEL1_DATA_SIZE) + ) + uri = 'api/v1/model1api/' + rv = self.auth_client_post( + client, + token, + uri, + item + ) + eq_(rv.status_code, 422) + data = json.loads(rv.data.decode('utf-8')) + eq_(data['message']['field_integer'][0], 'Not a valid integer.') + # Test string as integer + item = dict( + field_string=MODEL1_DATA_SIZE, + field_integer=MODEL1_DATA_SIZE, + field_float=float(MODEL1_DATA_SIZE) + ) + rv = self.auth_client_post( + client, + token, + uri, + item + ) + eq_(rv.status_code, 422) + data = json.loads(rv.data.decode('utf-8')) + eq_(data['message']['field_string'][0], 'Not a valid string.') + + def test_create_item_excluded_cols(self): + """ + REST Api: Test create with excluded columns + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + item = dict( + field_string="test{}".format(MODEL1_DATA_SIZE+1) + ) + uri = 'api/v1/model1apiexcludecols/' + rv = self.auth_client_post( + client, + token, + uri, + item + ) + eq_(rv.status_code, 201) + item = dict( + field_string="test{}".format(MODEL1_DATA_SIZE+2), + field_integer=MODEL1_DATA_SIZE+2 + ) + rv = self.auth_client_post( + client, + token, + uri, + item + ) + eq_(rv.status_code, 201) + model = (self.db.session.query(Model1) + .filter_by(field_string="test{}".format(MODEL1_DATA_SIZE+1)) + .first()) + eq_(model.field_integer, None) + eq_(model.field_float, None) + eq_(model.field_date, None) + + def test_create_item_with_enum(self): + """ + REST Api: Test create item with enum + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + item = dict( + enum2='e1' + ) + uri = 'api/v1/modelwithenumsapi/' + rv = self.auth_client_post( + client, + token, + uri, + item + ) + data = json.loads(rv.data.decode('utf-8')) + eq_(rv.status_code, 201) + model = self.db.session.query(ModelWithEnums).get(data['id']) + eq_(model.enum2, TmpEnum.e1) + + def test_get_list_col_function(self): + """ + REST Api: Test get list of objects with columns as functions + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + uri = 'api/v1/model1funcapi/' + rv = self.auth_client_get( + client, + token, + uri + ) + data = json.loads(rv.data.decode('utf-8')) + # Tests count property + eq_(data['count'], MODEL1_DATA_SIZE) + # Tests data result default page size + eq_(len(data[API_RESULT_RES_KEY]), self.model1api.page_size) + for i in range(1, self.model1api.page_size): + item = data[API_RESULT_RES_KEY][i - 1] + eq_( + item['full_concat'], "{}.{}.{}.{}".format( + "test" + str(i - 1), + i - 1, + float(i - 1), + None + ) + ) + + def test_openapi(self): + """ + REST Api: Test OpenAPI spec + """ + client = self.app.test_client() + token = self.login(client, USERNAME, PASSWORD) + uri = 'api/v1/_openapi' + rv = self.auth_client_get( + client, + token, + uri + ) + eq_(rv.status_code, 200) diff --git a/flask_appbuilder/tests/test_base.py b/flask_appbuilder/tests/test_base.py index 04ea6694b..9bdd3e8e2 100644 --- a/flask_appbuilder/tests/test_base.py +++ b/flask_appbuilder/tests/test_base.py @@ -5,36 +5,28 @@ import datetime import json import logging - -try: - import enum - _has_enum = True -except ImportError: - _has_enum = False - from nose.tools import eq_, ok_ -from sqlalchemy import Column, Integer, String, ForeignKey, Date, Float, Enum, DateTime -from sqlalchemy.orm import relationship from flask import redirect, request, session import jinja2 from flask_appbuilder import Model, SQLA from flask_appbuilder.models.sqla.filters import FilterStartsWith, FilterEqual -from flask_appbuilder.models.mixins import FileColumn, ImageColumn from flask_appbuilder.views import MasterDetailView, CompactCRUDMixin from flask_appbuilder.charts.views import (ChartView, TimeChartView, DirectChartView, GroupByChartView, DirectByChartView) from flask_appbuilder.models.group import aggregate_avg, aggregate_count, aggregate_sum - from flask_appbuilder.models.generic import PSSession from flask_appbuilder.models.generic.interface import GenericInterface from flask_appbuilder.models.generic import PSModel +from .sqla.models import Model1, Model2, Model3, ModelWithEnums, TmpEnum + logging.basicConfig(format='%(asctime)s:%(levelname)s:%(name)s:%(message)s') logging.getLogger().setLevel(logging.DEBUG) + """ Constant english display string from framework """ @@ -45,61 +37,11 @@ NOTNULL_VALIDATION_STRING = 'This field is required' DEFAULT_ADMIN_USER = 'admin' DEFAULT_ADMIN_PASSWORD = 'general' -REDIRECT_OBJ_ID = 99999 +REDIRECT_OBJ_ID = 1 log = logging.getLogger(__name__) -class Model1(Model): - id = Column(Integer, primary_key=True) - field_string = Column(String(50), unique=True, nullable=False) - field_integer = Column(Integer()) - field_float = Column(Float()) - field_date = Column(Date()) - field_file = FileColumn() - field_image = ImageColumn() - - def __repr__(self): - return str(self.field_string) - - -class Model2(Model): - id = Column(Integer, primary_key=True) - field_string = Column(String(50), unique=True, nullable=False) - field_integer = Column(Integer()) - field_float = Column(Float()) - field_date = Column(Date()) - excluded_string = Column(String(50), default='EXCLUDED') - default_string = Column(String(50), default='DEFAULT') - group_id = Column(Integer, ForeignKey('model1.id'), nullable=False) - group = relationship("Model1") - - def __repr__(self): - return str(self.field_string) - - def field_method(self): - return "field_method_value" - -class Model3(Model): - pk1 = Column(Integer(), primary_key=True) - pk2 = Column(DateTime(), primary_key=True) - field_string = Column(String(50), unique=True, nullable=False) - - def __repr__(self): - return str(self.field_string) - - -if _has_enum: - class TestEnum(enum.Enum): - e1 = 'a' - e2 = 2 - -class ModelWithEnums(Model): - id = Column(Integer, primary_key=True) - enum1 = Column(Enum('e1', 'e2')) - if _has_enum: - enum2 = Column(Enum(TestEnum)) - class FlaskTestCase(unittest.TestCase): def setUp(self): from flask import Flask @@ -114,6 +56,7 @@ def setUp(self): self.app.config['CSRF_ENABLED'] = False self.app.config['SECRET_KEY'] = 'thisismyscretkey' self.app.config['WTF_CSRF_ENABLED'] = False + self.app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False self.db = SQLA(self.app) self.appbuilder = AppBuilder(self.app, self.db.session) @@ -126,7 +69,6 @@ class PSView(ModelView): list_columns = ['UID', 'C', 'CMD', 'TIME'] search_columns = ['UID', 'C', 'CMD'] - class Model2View(ModelView): datamodel = SQLAInterface(Model2) list_columns = ['field_integer', 'field_float', 'field_string', 'field_method', 'group.field_string'] @@ -174,23 +116,19 @@ class Model1Filtered1View(ModelView): datamodel = SQLAInterface(Model1) base_filters = [['field_string', FilterStartsWith, 'a']] - class Model1MasterView(MasterDetailView): datamodel = SQLAInterface(Model1) related_views = [Model2View] - class Model1Filtered2View(ModelView): datamodel = SQLAInterface(Model1) base_filters = [['field_integer', FilterEqual, 0]] - class Model2ChartView(ChartView): datamodel = SQLAInterface(Model2) chart_title = 'Test Model1 Chart' group_by_columns = ['field_string'] - class Model2GroupByChartView(GroupByChartView): datamodel = SQLAInterface(Model2) chart_title = 'Test Model1 Chart' @@ -271,7 +209,14 @@ class ModelWithEnumsView(ModelView): self.appbuilder.add_view(PSView, "Generic DS PS View", category='PSView') role_admin = self.appbuilder.sm.find_role('Admin') - self.appbuilder.sm.add_user('admin','admin','user','admin@fab.org',role_admin,'general') + self.appbuilder.sm.add_user( + 'admin', + 'admin', + 'user', + 'admin@fab.org', + role_admin, + 'general' + ) def tearDown(self): self.appbuilder = None @@ -326,7 +271,11 @@ def insert_data2(self): self.db.session.rollback() def insert_data3(self): - model3 = Model3(pk1=3, pk2=datetime.datetime(2017, 3, 3), field_string='foo') + model3 = Model3( + pk1=3, + pk2=datetime.datetime(2017, 3, 3), + field_string='foo' + ) try: self.db.session.add(model3) self.db.session.commit() @@ -338,7 +287,7 @@ def test_fab_views(self): """ Test views creation and registration """ - eq_(len(self.appbuilder.baseviews), 32) # current minimal views are 12 + eq_(len(self.appbuilder.baseviews), 34) # current minimal views are 34 def test_back(self): """ @@ -435,15 +384,22 @@ def test_sec_reset_password(self): eq_(rv.status_code, 200) #Reset Password Admin - rv = client.get('/users/action/resetpasswords/1', follow_redirects=True) + rv = client.get( + '/users/action/resetpasswords/1', + follow_redirects=True + ) data = rv.data.decode('utf-8') ok_("Reset Password Form" in data) - rv = client.post('/resetmypassword/form', - data=dict(password=DEFAULT_ADMIN_PASSWORD, conf_password=DEFAULT_ADMIN_PASSWORD), - follow_redirects=True) + rv = client.post( + '/resetmypassword/form', + data=dict( + password=DEFAULT_ADMIN_PASSWORD, + conf_password=DEFAULT_ADMIN_PASSWORD + ), + follow_redirects=True + ) eq_(rv.status_code, 200) - def test_generic_interface(self): """ Test Generic Interface for generic-alter datasource @@ -453,7 +409,6 @@ def test_generic_interface(self): rv = client.get('/psview/list') data = rv.data.decode('utf-8') - def test_model_crud(self): """ Test Model add, delete, edit @@ -531,27 +486,21 @@ def test_model_crud_with_enum(self): client = self.app.test_client() rv = self.login(client, DEFAULT_ADMIN_USER, DEFAULT_ADMIN_PASSWORD) - data = {'enum1': u'e1'} - if _has_enum: - data['enum2'] = 'e1' + data = {'enum1': u'e1', 'enum2': 'e1'} rv = client.post('/modelwithenumsview/add', data=data, follow_redirects=True) eq_(rv.status_code, 200) model = self.db.session.query(ModelWithEnums).first() eq_(model.enum1, u'e1') - if _has_enum: - eq_(model.enum2, TestEnum.e1) + eq_(model.enum2, TmpEnum.e1) - data = {'enum1': u'e2'} - if _has_enum: - data['enum2'] = 'e2' + data = {'enum1': u'e2', 'enum2': 'e2'} rv = client.post('/modelwithenumsview/edit/1', data=data, follow_redirects=True) eq_(rv.status_code, 200) model = self.db.session.query(ModelWithEnums).first() eq_(model.enum1, u'e2') - if _has_enum: - eq_(model.enum2, TestEnum.e2) + eq_(model.enum2, TmpEnum.e2) rv = client.get('/modelwithenumsview/delete/1', follow_redirects=True) eq_(rv.status_code, 200) diff --git a/flask_appbuilder/tests/test_mongoengine.py b/flask_appbuilder/tests/test_mongoengine.py index 655f18ade..574cc078a 100644 --- a/flask_appbuilder/tests/test_mongoengine.py +++ b/flask_appbuilder/tests/test_mongoengine.py @@ -236,7 +236,7 @@ def test_fab_views(self): """ Test views creation and registration """ - eq_(len(self.appbuilder.baseviews), 24) # current minimal views are 12 + eq_(len(self.appbuilder.baseviews), 26) # current minimal views are 26 def test_index(self): """ diff --git a/flask_appbuilder/views.py b/flask_appbuilder/views.py index 1d783e026..cb4da56e2 100644 --- a/flask_appbuilder/views.py +++ b/flask_appbuilder/views.py @@ -164,6 +164,7 @@ def _get_modelview_urls(self, modelview_urls=None): @has_access_api @permission_name('list') def api(self): + log.warning("This API is deprecated and will be removed on 1.15.X") view_name = self.__class__.__name__ api_urls = self._get_api_urls() modelview_urls = self._get_modelview_urls() @@ -206,6 +207,7 @@ def api(self): def api_read(self): """ """ + log.warning("This API is deprecated and will be removed on 1.15.X") # Get arguments for ordering if get_order_args().get(self.__class__.__name__): order_column, order_direction = get_order_args().get(self.__class__.__name__) @@ -247,6 +249,7 @@ def show_item_dict(self, item): def api_get(self, pk): """ """ + log.warning("This API is deprecated and will be removed on 1.15.X") # Get arguments for ordering item = self.datamodel.get(pk, self._base_filters) if not item: @@ -264,6 +267,7 @@ def api_get(self, pk): @has_access_api @permission_name('add') def api_create(self): + log.warning("This API is deprecated and will be removed on 1.15.X") get_filter_args(self._filters) exclude_cols = self._filters.get_relation_cols() form = self.add_form.refresh() @@ -294,6 +298,7 @@ def api_create(self): @has_access_api @permission_name('edit') def api_update(self, pk): + log.warning("This API is deprecated and will be removed on 1.15.X") get_filter_args(self._filters) exclude_cols = self._filters.get_relation_cols() @@ -340,6 +345,7 @@ def api_update(self, pk): @has_access_api @permission_name('delete') def api_delete(self, pk): + log.warning("This API is deprecated and will be removed on 1.15.X") item = self.datamodel.get(pk, self._base_filters) if not item: abort(404) @@ -382,6 +388,7 @@ def api_column_add(self, col_name): :param col_name: The related column name :return: JSON response """ + log.warning("This API is deprecated and will be removed on 1.15.X") filter_rel_fields = None if self.add_form_query_rel_fields: filter_rel_fields = self.add_form_query_rel_fields.get(col_name) @@ -402,6 +409,7 @@ def api_column_edit(self, col_name): :param col_name: The related column name :return: JSON response """ + log.warning("This API is deprecated and will be removed on 1.15.X") filter_rel_fields = None if self.edit_form_query_rel_fields: filter_rel_fields = self.edit_form_query_rel_fields @@ -416,6 +424,7 @@ def api_column_edit(self, col_name): def api_readvalues(self): """ """ + log.warning("This API is deprecated and will be removed on 1.15.X") # Get arguments for ordering if get_order_args().get(self.__class__.__name__): order_column, order_direction = get_order_args().get(self.__class__.__name__) @@ -436,7 +445,6 @@ def api_readvalues(self): return response - class ModelView(RestCRUDView): """ This is the CRUD generic view. @@ -444,7 +452,7 @@ class ModelView(RestCRUDView): delete, show, and list from your database tables, inherit your views from this class. Notice that this class inherits from BaseCRUDView and BaseModelView - so all properties from the parent class can be overriden. + so all properties from the parent class can be overridden. """ def __init__(self, **kwargs): diff --git a/requirements.txt b/requirements.txt index 51a98f3fe..a53843c90 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,6 +23,8 @@ Jinja2==2.10 MarkupSafe==1.1.1 marshmallow==2.18.0 marshmallow-sqlalchemy==0.15.0 +marshmallow-enum==1.4.1 +apispec==1.1.1 mockldap==0.3.0 mongoengine==0.7.10 nose==1.3.7 @@ -31,6 +33,7 @@ prison==0.1.0 pyasn1==0.4.5 pyasn1-modules==0.2.4 PyJWT==1.7.1 +jsonschema==3.0.1 pymongo==2.8.1 python-dateutil==2.8.0 pytz==2018.9 @@ -39,4 +42,3 @@ six==1.12.0 SQLAlchemy==1.3.1 Werkzeug==0.14.1 WTForms==2.2.1 - diff --git a/rtd_requirements.txt b/rtd_requirements.txt index f3ffed03f..b0adfe879 100644 --- a/rtd_requirements.txt +++ b/rtd_requirements.txt @@ -8,4 +8,11 @@ Flask-Login>=0.3,<0.5 Flask-OpenID>=1.2.5,<2 Flask-SQLAlchemy>=2.3,<3 Flask-WTF>=0.14.2,<1 +flask-marshmallow==0.9.0 +Flask-JWT-Extended>=3.18,<4 +marshmallow==2.18.0 +marshmallow-sqlalchemy==0.15.0 +prison==0.1.0 +apispec==1.1.1 +jsonschema==3.0.1 PyJWT>=1.7.1 diff --git a/setup.py b/setup.py index 58323bffe..ffb95b86d 100644 --- a/setup.py +++ b/setup.py @@ -37,14 +37,21 @@ def desc(): install_requires=[ 'colorama>=0.3.9,<1', 'click>=6.7,<8', + 'apispec[yaml]>=1.1.1<2', 'Flask>=0.12,<2', 'Flask-Babel>=0.11.1,<1', 'Flask-Login>=0.3,<0.5', 'Flask-OpenID>=1.2.5,<2', 'Flask-SQLAlchemy>=2.3,<3', 'Flask-WTF>=0.14.2,<1', + 'Flask-JWT-Extended>=3.18,<4', 'python-dateutil>=2.3,<3', - 'PyJWT>=1.7.1', + 'marshmallow>=2.18.0,<2.20', + 'marshmallow-enum>=1.4.1,<2' + 'marshmallow-sqlalchemy>=0.16.1<1', + 'prison==0.1.0', + 'jsonschema>=3.0.1<4', + 'PyJWT>=1.7.1' ], tests_require=[ 'nose>=1.0', diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..2bd467f24 --- /dev/null +++ b/tox.ini @@ -0,0 +1,38 @@ +[flake8] +accept-encodings = utf-8 +exclude = + .tox + build + docs + examples + flask_appbuilder/templates + flask_appbuilder/static + venv +ignore = + FI12 + FI15 + FI16 + FI17 + FI50 + FI51 + FI53 + FI54 + W504 + W605 +import-order-style = google +max-line-length = 90 +require-code = true + +[testenv:flake8] +commands = + flake8 {toxinidir}/ + +[testenv:apitest] +commands = + nosetests -v --with-coverage --cover-package flask_appbuilder.api flask_appbuilder.tests.test_api + +[tox] +envlist = + flake8 + apitest +