diff --git a/dashboard/vault.py b/dashboard/vault.py
index ac48e57d8..2547fec23 100644
--- a/dashboard/vault.py
+++ b/dashboard/vault.py
@@ -85,7 +85,7 @@ def init_releases(vault):
 
 def _make_module(module_id, text, modules, tag):
     return {'id': module_id, 'text': text,
-            'modules': modules, 'tag': tag}
+            'modules': set(modules), 'tag': tag}
 
 
 def init_module_groups(vault):
diff --git a/dashboard/web.py b/dashboard/web.py
index a6cb0e45c..1b1a92047 100644
--- a/dashboard/web.py
+++ b/dashboard/web.py
@@ -266,6 +266,12 @@ def get_modules_json(records):
     if tags:
         module_ids = set(module_id for module_id in module_ids
                          if module_id_index[module_id].get('tag') in tags)
+    # keep only modules that are in project type completely
+    pts = parameters.get_parameter({}, 'project_type', 'project_types')
+    if pts:
+        m = set(vault.resolve_project_types(pts))
+        module_ids = set(module_id for module_id in module_ids
+                         if module_id_index[module_id]['modules'] <= m)
 
     query = (flask.request.args.get('query') or '').lower()
     matched = []
@@ -300,7 +306,9 @@ def get_module(module):
     module_id_index = vault.get_vault()['module_id_index']
     module = module.lower()
     if module in module_id_index:
-        return module_id_index[module]
+        return {'id': module_id_index[module]['id'],
+                'text': module_id_index[module]['text'],
+                'tag': module_id_index[module]['tag']}
     flask.abort(404)
 
 
diff --git a/etc/default_data.json b/etc/default_data.json
index f1bb87806..335d5c856 100644
--- a/etc/default_data.json
+++ b/etc/default_data.json
@@ -6143,7 +6143,7 @@
         {
             "id": "all",
             "title": "All",
-            "modules": ["openstack", "openstack-infra", "openstack-dev", "stackforge"]
+            "modules": ["openstack", "openstack-infra", "openstack-dev", "stackforge", "unknown"]
         },
         {
             "id": "openstack",
diff --git a/etc/test_default_data.json b/etc/test_default_data.json
index 3a5cd0509..7397169e5 100644
--- a/etc/test_default_data.json
+++ b/etc/test_default_data.json
@@ -152,7 +152,7 @@
         {
             "id": "all",
             "title": "All",
-            "modules": ["openstack", "openstack-infra", "openstack-dev", "stackforge"]
+            "modules": ["openstack", "openstack-infra", "openstack-dev", "stackforge", "unknown"]
         },
         {
             "id": "openstack",
diff --git a/tests/api/test_modules.py b/tests/api/test_modules.py
index 6fcf8860c..0451cd199 100644
--- a/tests/api/test_modules.py
+++ b/tests/api/test_modules.py
@@ -28,27 +28,45 @@ class TestAPIModules(test_api.TestAPI):
                             'uri': 'git://github.com/openstack/glance.git'}],
                  'module_groups': {'nova-group': {
                      'module_group_name': 'nova-group',
-                     'modules': ['nova', 'python-novaclient']}},
+                     'modules': ['nova', 'nova-cli']}},
                  'project_types': [
                      {'id': 'all', 'title': 'All',
+                      'modules': ['nova', 'glance', 'nova-cli']},
+                     {'id': 'integrated', 'title': 'Integrated',
                       'modules': ['nova', 'glance']}]},
                 test_api.make_records(record_type=['commit'],
-                                      module=['glance', 'nova'])):
+                                      module=['glance', 'nova', 'nova-cli'])):
 
             response = self.app.get('/api/1.0/modules?project_type=all')
             modules = json.loads(response.data)['modules']
             self.assertEqual(
                 [{'id': 'glance', 'text': 'glance', 'tag': 'module'},
                  {'id': 'nova', 'text': 'nova', 'tag': 'module'},
+                 {'id': 'nova-cli', 'text': 'nova-cli', 'tag': 'module'},
                  {'id': 'nova-group', 'text': 'nova-group', 'tag': 'group'}],
-                modules)
+                modules,
+                message='Expected modules belonging to project type plus '
+                        'module groups that are completely within '
+                        'project type')
+
+            response = self.app.get('/api/1.0/modules?module=nova-group&'
+                                    'project_type=integrated')
+            modules = json.loads(response.data)['modules']
+            self.assertEqual(
+                [{'id': 'glance', 'text': 'glance', 'tag': 'module'},
+                 {'id': 'nova', 'text': 'nova', 'tag': 'module'}],
+                modules,
+                message='Expected modules belonging to project type plus '
+                        'module groups that are completely within '
+                        'project type')
 
             response = self.app.get('/api/1.0/modules?query=glance&'
                                     'project_type=all')
             modules = json.loads(response.data)['modules']
             self.assertEqual(
                 [{'id': 'glance', 'text': 'glance', 'tag': 'module'}],
-                modules)
+                modules,
+                message='Expected modules which name contains query')
 
     def test_get_module(self):
         with test_api.make_runtime_storage(
@@ -62,11 +80,10 @@ class TestAPIModules(test_api.TestAPI):
             response = self.app.get('/api/1.0/modules/nova')
             module = json.loads(response.data)['module']
             self.assertEqual(
-                {'id': 'nova', 'modules': ['nova'], 'text': 'nova',
-                 'tag': 'module'}, module)
+                {'id': 'nova', 'text': 'nova', 'tag': 'module'}, module)
 
             response = self.app.get('/api/1.0/modules/nova-group')
             module = json.loads(response.data)['module']
             self.assertEqual(
-                {'tag': 'group', 'id': 'nova-group', 'text': 'nova-group',
-                 'modules': ['nova', 'python-novaclient']}, module)
+                {'tag': 'group', 'id': 'nova-group', 'text': 'nova-group'},
+                module)