Added display_type_name field in BaseArtifact
display_type_name provides admin a facility to define a more user friendly type_name for artifact. Change-Id: I8b1b08e4fa647c87c65f7af94f8f49d6269e03cf
This commit is contained in:
parent
d1de4d1c7b
commit
a81783e682
@ -320,7 +320,7 @@ class ArtifactsController(api_versioning.VersionedResource):
|
||||
if not values.get('name'):
|
||||
msg = _("Name must be specified at creation.")
|
||||
raise exc.BadRequest(msg)
|
||||
for field in ('visibility', 'status'):
|
||||
for field in ('visibility', 'status', 'display_type_name'):
|
||||
if field in values:
|
||||
msg = _("%s is not allowed in a request at creation.") % field
|
||||
raise exc.BadRequest(msg)
|
||||
@ -385,7 +385,8 @@ class ArtifactsController(api_versioning.VersionedResource):
|
||||
artifacts = artifacts_data["artifacts"]
|
||||
result = {'artifacts': artifacts,
|
||||
'type_name': type_name,
|
||||
'total_count': artifacts_data['total_count']}
|
||||
'total_count': artifacts_data['total_count'],
|
||||
'display_type_name': artifacts_data['display_type_name']}
|
||||
if len(artifacts) != 0 and len(artifacts) == limit:
|
||||
result['next_marker'] = artifacts[-1]['id']
|
||||
return result
|
||||
@ -545,7 +546,8 @@ class ResponseSerializer(api_versioning.VersionedResource,
|
||||
'artifacts': af_list['artifacts'],
|
||||
'first': '/artifacts/%s' % type_name,
|
||||
'schema': '/schemas/%s' % type_name,
|
||||
'total_count': af_list['total_count']
|
||||
'total_count': af_list['total_count'],
|
||||
'display_type_name': af_list['display_type_name']
|
||||
}
|
||||
if query:
|
||||
body['first'] = '%s?%s' % (body['first'], query)
|
||||
|
@ -0,0 +1,45 @@
|
||||
# Copyright 2018 OpenStack Foundation.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""added display name
|
||||
|
||||
Revision ID: 005
|
||||
Revises: 004
|
||||
Create Date: 2018-03-13 14:32:33.765690
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '005'
|
||||
down_revision = '004'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('glare_artifacts', sa.Column('display_type_name',
|
||||
sa.String(255),
|
||||
nullable=True))
|
||||
op.create_index('ix_glare_artifact_display_name',
|
||||
'glare_artifacts',
|
||||
['display_type_name']
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.batch_alter_table('glare_artifacts') as batch_op:
|
||||
batch_op.drop_index('ix_glare_artifact_display_name')
|
||||
batch_op.drop_column('display_type_name')
|
@ -50,7 +50,8 @@ options.set_defaults(CONF)
|
||||
|
||||
BASE_ARTIFACT_PROPERTIES = ('id', 'visibility', 'created_at', 'updated_at',
|
||||
'activated_at', 'owner', 'status', 'description',
|
||||
'name', 'type_name', 'version')
|
||||
'name', 'type_name', 'version',
|
||||
'display_type_name')
|
||||
|
||||
_FACADE = None
|
||||
_LOCK = threading.Lock()
|
||||
|
@ -99,6 +99,7 @@ class Artifact(BASE, ArtifactBase):
|
||||
Index('ix_glare_artifact_status', 'status'),
|
||||
Index('ix_glare_artifact_owner', 'owner'),
|
||||
Index('ix_glare_artifact_visibility', 'visibility'),
|
||||
Index('ix_glare_artifact_display_name', 'display_type_name'),
|
||||
{'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8'})
|
||||
__protected_attributes__ = set(["created_at", "updated_at"])
|
||||
|
||||
@ -122,6 +123,7 @@ class Artifact(BASE, ArtifactBase):
|
||||
updated_at = Column(DateTime, default=lambda: timeutils.utcnow(),
|
||||
nullable=False, onupdate=lambda: timeutils.utcnow())
|
||||
activated_at = Column(DateTime)
|
||||
display_type_name = Column(String(255), nullable=True)
|
||||
|
||||
def to_dict(self):
|
||||
d = super(Artifact, self).to_dict()
|
||||
|
@ -349,6 +349,9 @@ class Engine(object):
|
||||
context, filters, marker, limit, sort, latest, list_all_artifacts)
|
||||
artifacts_data["artifacts"] = [af.to_dict()
|
||||
for af in artifacts_data["artifacts"]]
|
||||
artifacts_data['display_type_name'] = \
|
||||
artifact_type.get_display_type_name()
|
||||
|
||||
return artifacts_data
|
||||
|
||||
@staticmethod
|
||||
|
@ -30,6 +30,14 @@ class All(base.BaseArtifact):
|
||||
'type_name': Field(fields.StringField,
|
||||
description="Name of artifact type.",
|
||||
sortable=True),
|
||||
'display_type_name': Field(fields.StringField,
|
||||
description="Display name of "
|
||||
"artifact type.",
|
||||
sortable=True,
|
||||
filter_ops=(wrappers.FILTER_LIKE,
|
||||
wrappers.FILTER_EQ,
|
||||
wrappers.FILTER_NEQ,
|
||||
wrappers.FILTER_IN))
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@ -51,6 +59,10 @@ class All(base.BaseArtifact):
|
||||
def get_type_name(cls):
|
||||
return "all"
|
||||
|
||||
@classmethod
|
||||
def get_display_type_name(cls):
|
||||
return "All Artifacts"
|
||||
|
||||
def to_dict(self):
|
||||
# Use specific method of artifact type to convert it to dict
|
||||
values = self.obj_to_primitive()['versioned_object.data']
|
||||
|
@ -245,6 +245,16 @@ Possible values:
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
def get_display_type_name(cls):
|
||||
"""
|
||||
Provides verbose Artifact type name which any external user can
|
||||
understand easily.
|
||||
|
||||
:return: general purpose name for Artifact
|
||||
"""
|
||||
return None
|
||||
|
||||
def create(self, context):
|
||||
"""Create new artifact in Glare repo.
|
||||
|
||||
@ -253,6 +263,7 @@ Possible values:
|
||||
"""
|
||||
values = self.obj_changes_to_primitive()
|
||||
values['type_name'] = self.get_type_name()
|
||||
values['display_type_name'] = self.get_display_type_name()
|
||||
|
||||
LOG.debug("Sending request to create artifact of type '%(type_name)s'."
|
||||
" New values are %(values)s",
|
||||
|
@ -29,3 +29,7 @@ class HeatEnvironment(base.BaseArtifact):
|
||||
@classmethod
|
||||
def get_type_name(cls):
|
||||
return "heat_environments"
|
||||
|
||||
@classmethod
|
||||
def get_display_type_name(cls):
|
||||
return "Heat Environments"
|
||||
|
@ -47,3 +47,7 @@ class HeatTemplate(base.BaseArtifact):
|
||||
@classmethod
|
||||
def get_type_name(cls):
|
||||
return "heat_templates"
|
||||
|
||||
@classmethod
|
||||
def get_display_type_name(cls):
|
||||
return "Heat Templates"
|
||||
|
@ -88,3 +88,7 @@ class Image(base.BaseArtifact):
|
||||
@classmethod
|
||||
def get_type_name(cls):
|
||||
return "images"
|
||||
|
||||
@classmethod
|
||||
def get_display_type_name(cls):
|
||||
return "Images"
|
||||
|
@ -58,3 +58,7 @@ class MuranoPackage(base.BaseArtifact):
|
||||
@classmethod
|
||||
def get_type_name(cls):
|
||||
return "murano_packages"
|
||||
|
||||
@classmethod
|
||||
def get_display_type_name(cls):
|
||||
return "Murano packages"
|
||||
|
@ -34,6 +34,10 @@ class Secret(base_artifact.BaseArtifact):
|
||||
def get_type_name(cls):
|
||||
return "secrets"
|
||||
|
||||
@classmethod
|
||||
def get_display_type_name(cls):
|
||||
return "Secrets"
|
||||
|
||||
fields = {
|
||||
'payload': Blob( # The encrypted secret data
|
||||
description="The secret's data to be stored"
|
||||
|
@ -33,3 +33,7 @@ class TOSCATemplate(base.BaseArtifact):
|
||||
@classmethod
|
||||
def get_type_name(cls):
|
||||
return "tosca_templates"
|
||||
|
||||
@classmethod
|
||||
def get_display_type_name(cls):
|
||||
return "TOSCA Templates"
|
||||
|
@ -61,6 +61,30 @@ class TestAll(base.TestArtifact):
|
||||
self.assertEqual(54, len(res))
|
||||
self.assertEqual(sorted(res, key=lambda x: x['type_name']), res)
|
||||
|
||||
# get all artifacts Sorted in Asc order based on display_type_name
|
||||
url = '/all?sort=display_type_name:asc&limit=100'
|
||||
res = self.get(url=url, status=200)['artifacts']
|
||||
self.assertEqual(54, len(res))
|
||||
self.assertEqual(sorted(res, key=lambda x: x['display_type_name']),
|
||||
res)
|
||||
|
||||
# get all artifacts sorted in desc order based on display_type_name
|
||||
url = '/all?sort=display_type_name:desc&limit=100'
|
||||
res = self.get(url=url, status=200)['artifacts']
|
||||
self.assertEqual(54, len(res))
|
||||
self.assertEqual(sorted(res, key=lambda x: x['display_type_name'],
|
||||
reverse=True), res)
|
||||
|
||||
# get Heat Template like only
|
||||
url = '/all?display_type_name=like:Heat%&sort=display_type_name:asc'
|
||||
res = self.get(url=url, status=200)['artifacts']
|
||||
self.assertEqual(18, len(res))
|
||||
for art in res:
|
||||
self.assertEqual('Heat', art['display_type_name'][:4])
|
||||
|
||||
# TODO(kushalagrawal): Need to Add test case for display_type_name with
|
||||
# null once https://bugs.launchpad.net/glare/+bug/1741400 is resolved
|
||||
|
||||
def test_all_readonlyness(self):
|
||||
self.create_artifact(data={'name': 'all'}, type_name='all', status=403)
|
||||
art = self.create_artifact(data={'name': 'image'}, type_name='images')
|
||||
|
@ -45,7 +45,8 @@ default_store = database
|
||||
'artifacts': [],
|
||||
'schema': '/schemas/sample_artifact',
|
||||
'type_name': 'sample_artifact',
|
||||
'total_count': 0}
|
||||
'total_count': 0,
|
||||
'display_type_name': 'Sample Artifact'}
|
||||
self.assertEqual(expected, response)
|
||||
|
||||
# Create a test artifact
|
||||
|
@ -653,6 +653,12 @@ class TestList(base.TestArtifact):
|
||||
self.assertEqual(art1, result['artifacts'][0])
|
||||
self.assertEqual(response_url, result['first'])
|
||||
|
||||
def test_list_response_attributes(self):
|
||||
url = '/sample_artifact'
|
||||
res = self.get(url=url, status=200)
|
||||
self.assertEqual(res['total_count'], 0)
|
||||
self.assertEqual(res['display_type_name'], "Sample Artifact")
|
||||
|
||||
|
||||
class TestBlobs(base.TestArtifact):
|
||||
def test_blob_dicts(self):
|
||||
@ -663,7 +669,8 @@ class TestBlobs(base.TestArtifact):
|
||||
'artifacts': [],
|
||||
'schema': '/schemas/sample_artifact',
|
||||
'type_name': 'sample_artifact',
|
||||
'total_count': 0}
|
||||
'total_count': 0,
|
||||
'display_type_name': 'Sample Artifact'}
|
||||
self.assertEqual(expected, response)
|
||||
|
||||
# Create a test artifact
|
||||
@ -1225,6 +1232,10 @@ class TestArtifactOps(base.TestArtifact):
|
||||
self.create_artifact(data={"name": "test_af",
|
||||
"string_required": "test_str"})
|
||||
|
||||
# Check we cannot create data with display_type_name.
|
||||
self.create_artifact(data={"display_type_name": "Sample Artifact",
|
||||
"name": "Invalid_data"}, status=400)
|
||||
|
||||
def test_activate(self):
|
||||
# create artifact to update
|
||||
private_art = self.create_artifact(
|
||||
|
@ -932,6 +932,11 @@ fixtures = {
|
||||
u'maxLength': 255,
|
||||
u'sortable': True,
|
||||
u'type': [u'string', u'null']},
|
||||
u'display_type_name': {
|
||||
u'description': u'Display name of artifact type.',
|
||||
u'filter_ops': [u'like', u'eq', u'neq', u'in'],
|
||||
u'glareType': u'String', u'maxLength': 255,
|
||||
u'sortable': True, u'type': [u'string', u'null']}
|
||||
|
||||
}),
|
||||
u'required': [u'name'],
|
||||
|
@ -74,6 +74,10 @@ class HookChecker(base.BaseArtifact):
|
||||
def get_type_name(cls):
|
||||
return "hooks_artifact"
|
||||
|
||||
@classmethod
|
||||
def get_display_type_name(cls):
|
||||
return "Hooks Artifact"
|
||||
|
||||
@classmethod
|
||||
def pre_create_hook(cls, context, af):
|
||||
# create a temporary file and set the path to artifact field
|
||||
|
@ -139,6 +139,10 @@ class SampleArtifact(base_artifact.BaseArtifact):
|
||||
def get_type_name(cls):
|
||||
return "sample_artifact"
|
||||
|
||||
@classmethod
|
||||
def get_display_type_name(cls):
|
||||
return "Sample Artifact"
|
||||
|
||||
def to_dict(self):
|
||||
res = self.obj_to_primitive()['versioned_object.data']
|
||||
res['__some_meta_information__'] = res['name'].upper()
|
||||
|
@ -247,6 +247,33 @@ class GlareMigrationsCheckers(object):
|
||||
self.assert_table(engine, 'glare_quotas', quota_indices,
|
||||
quota_columns)
|
||||
|
||||
def _check_005(self, engine, data):
|
||||
artifacts_indices = [('ix_glare_artifact_name_and_version',
|
||||
['name', 'version_prefix', 'version_suffix']),
|
||||
('ix_glare_artifact_type',
|
||||
['type_name']),
|
||||
('ix_glare_artifact_status', ['status']),
|
||||
('ix_glare_artifact_visibility', ['visibility']),
|
||||
('ix_glare_artifact_owner', ['owner']),
|
||||
('ix_glare_artifact_display_name',
|
||||
['display_type_name'])]
|
||||
artifacts_columns = ['id',
|
||||
'name',
|
||||
'type_name',
|
||||
'version_prefix',
|
||||
'version_suffix',
|
||||
'version_meta',
|
||||
'description',
|
||||
'visibility',
|
||||
'status',
|
||||
'owner',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'activated_at',
|
||||
'display_type_name']
|
||||
self.assert_table(engine, 'glare_artifacts', artifacts_indices,
|
||||
artifacts_columns)
|
||||
|
||||
|
||||
class TestMigrationsMySQL(GlareMigrationsCheckers,
|
||||
WalkVersionsMixin,
|
||||
|
@ -37,6 +37,10 @@ class Unpacker(base.BaseArtifact):
|
||||
def get_type_name(cls):
|
||||
return "unpacking_artifact"
|
||||
|
||||
@classmethod
|
||||
def get_display_type_name(cls):
|
||||
return "Unpacking Artifact"
|
||||
|
||||
@classmethod
|
||||
def pre_upload_hook(cls, context, af, field_name, blob_key, fd):
|
||||
flobj = io.BytesIO(fd.read(cls.MAX_BLOB_SIZE))
|
||||
|
Loading…
x
Reference in New Issue
Block a user