From bef8a7a63098f903bcd2a9d42f2f37458602340f Mon Sep 17 00:00:00 2001
From: Antonia Gaete <antoniagaete@osuosl.org>
Date: Wed, 26 Jun 2024 15:54:44 +0000
Subject: [PATCH] identity: Migrate 'application credential' commands to SDK

Change-Id: Iba3fee2672d32266623c6f367beaabe84bd3d24e
---
 .../identity/v3/application_credential.py     | 156 +++++--
 .../v3/test_application_credential.py         |  14 +-
 .../v3/test_application_credential.py         | 441 +++++++++---------
 ...on-credential-to-sdk-c79d8dfc3c8e1d9f.yaml |   9 +
 4 files changed, 344 insertions(+), 276 deletions(-)
 create mode 100644 releasenotes/notes/migrate-application-credential-to-sdk-c79d8dfc3c8e1d9f.yaml

diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py
index cffab99274..035be2d212 100644
--- a/openstackclient/identity/v3/application_credential.py
+++ b/openstackclient/identity/v3/application_credential.py
@@ -18,6 +18,7 @@
 import datetime
 import json
 import logging
+import uuid
 
 from osc_lib.command import command
 from osc_lib import exceptions
@@ -30,6 +31,30 @@ from openstackclient.identity import common
 LOG = logging.getLogger(__name__)
 
 
+# TODO(stephenfin): Move this to osc_lib since it's useful elsewhere
+def is_uuid_like(value) -> bool:
+    """Returns validation of a value as a UUID.
+
+    :param val: Value to verify
+    :type val: string
+    :returns: bool
+
+    .. versionchanged:: 1.1.1
+       Support non-lowercase UUIDs.
+    """
+    try:
+        formatted_value = (
+            value.replace('urn:', '')
+            .replace('uuid:', '')
+            .strip('{}')
+            .replace('-', '')
+            .lower()
+        )
+        return str(uuid.UUID(value)).replace('-', '') == formatted_value
+    except (TypeError, ValueError, AttributeError):
+        return False
+
+
 class CreateApplicationCredential(command.ShowOne):
     _description = _("Create new application credential")
 
@@ -105,19 +130,16 @@ class CreateApplicationCredential(command.ShowOne):
         return parser
 
     def take_action(self, parsed_args):
-        identity_client = self.app.client_manager.identity
+        identity_client = self.app.client_manager.sdk_connection.identity
+        conn = self.app.client_manager.sdk_connection
+        user_id = conn.config.get_auth().get_user_id(conn.identity)
 
         role_ids = []
         for role in parsed_args.role:
-            # A user can only create an application credential for themself,
-            # not for another user even as an admin, and only on the project to
-            # which they are currently scoped with a subset of the role
-            # assignments they have on that project. Don't bother trying to
-            # look up roles via keystone, just introspect the token.
-            role_id = common._get_token_resource(
-                identity_client, "roles", role
-            )
-            role_ids.append(role_id)
+            if is_uuid_like(role):
+                role_ids.append({'id': role})
+            else:
+                role_ids.append({'name': role})
 
         expires_at = None
         if parsed_args.expiration:
@@ -144,10 +166,10 @@ class CreateApplicationCredential(command.ShowOne):
                     )
                     raise exceptions.CommandError(msg)
         else:
-            access_rules = None
+            access_rules = []
 
-        app_cred_manager = identity_client.application_credentials
-        application_credential = app_cred_manager.create(
+        application_credential = identity_client.create_application_credential(
+            user_id,
             parsed_args.name,
             roles=role_ids,
             expires_at=expires_at,
@@ -157,14 +179,32 @@ class CreateApplicationCredential(command.ShowOne):
             access_rules=access_rules,
         )
 
-        application_credential._info.pop('links', None)
-
         # Format roles into something sensible
-        roles = application_credential._info.pop('roles')
-        msg = ' '.join(r['name'] for r in roles)
-        application_credential._info['roles'] = msg
+        if application_credential['roles']:
+            roles = application_credential['roles']
+            msg = ' '.join(r['name'] for r in roles)
+            application_credential['roles'] = msg
 
-        return zip(*sorted(application_credential._info.items()))
+        columns = (
+            'ID',
+            'Name',
+            'Description',
+            'Project ID',
+            'Roles',
+            'Unrestricted',
+            'Access Rules',
+            'Expires At',
+            'Secret',
+        )
+        return (
+            columns,
+            (
+                utils.get_dict_properties(
+                    application_credential,
+                    columns,
+                )
+            ),
+        )
 
 
 class DeleteApplicationCredential(command.Command):
@@ -181,15 +221,19 @@ class DeleteApplicationCredential(command.Command):
         return parser
 
     def take_action(self, parsed_args):
-        identity_client = self.app.client_manager.identity
+        identity_client = self.app.client_manager.sdk_connection.identity
+        conn = self.app.client_manager.sdk_connection
+        user_id = conn.config.get_auth().get_user_id(conn.identity)
 
         errors = 0
         for ac in parsed_args.application_credential:
             try:
-                app_cred = utils.find_resource(
-                    identity_client.application_credentials, ac
+                app_cred = identity_client.find_application_credential(
+                    user_id, ac
+                )
+                identity_client.delete_application_credential(
+                    user_id, app_cred.id
                 )
-                identity_client.application_credentials.delete(app_cred.id)
             except Exception as e:
                 errors += 1
                 LOG.error(
@@ -223,16 +267,36 @@ class ListApplicationCredential(command.Lister):
         return parser
 
     def take_action(self, parsed_args):
-        identity_client = self.app.client_manager.identity
+        identity_client = self.app.client_manager.sdk_connection.identity
         if parsed_args.user:
             user_id = common.find_user(
                 identity_client, parsed_args.user, parsed_args.user_domain
             ).id
         else:
-            user_id = None
+            conn = self.app.client_manager.sdk_connection
+            user_id = conn.config.get_auth().get_user_id(conn.identity)
 
-        columns = ('ID', 'Name', 'Project ID', 'Description', 'Expires At')
-        data = identity_client.application_credentials.list(user=user_id)
+        data = identity_client.application_credentials(user=user_id)
+
+        data_formatted = []
+        for ac in data:
+            # Format roles into something sensible
+            roles = ac['roles']
+            msg = ' '.join(r['name'] for r in roles)
+            ac['roles'] = msg
+
+            data_formatted.append(ac)
+
+        columns = (
+            'ID',
+            'Name',
+            'Description',
+            'Project ID',
+            'Roles',
+            'Unrestricted',
+            'Access Rules',
+            'Expires At',
+        )
         return (
             columns,
             (
@@ -241,7 +305,7 @@ class ListApplicationCredential(command.Lister):
                     columns,
                     formatters={},
                 )
-                for s in data
+                for s in data_formatted
             ),
         )
 
@@ -259,17 +323,35 @@ class ShowApplicationCredential(command.ShowOne):
         return parser
 
     def take_action(self, parsed_args):
-        identity_client = self.app.client_manager.identity
-        app_cred = utils.find_resource(
-            identity_client.application_credentials,
-            parsed_args.application_credential,
+        identity_client = self.app.client_manager.sdk_connection.identity
+        conn = self.app.client_manager.sdk_connection
+        user_id = conn.config.get_auth().get_user_id(conn.identity)
+
+        app_cred = identity_client.find_application_credential(
+            user_id, parsed_args.application_credential
         )
 
-        app_cred._info.pop('links', None)
-
         # Format roles into something sensible
-        roles = app_cred._info.pop('roles')
+        roles = app_cred['roles']
         msg = ' '.join(r['name'] for r in roles)
-        app_cred._info['roles'] = msg
+        app_cred['roles'] = msg
 
-        return zip(*sorted(app_cred._info.items()))
+        columns = (
+            'ID',
+            'Name',
+            'Description',
+            'Project ID',
+            'Roles',
+            'Unrestricted',
+            'Access Rules',
+            'Expires At',
+        )
+        return (
+            columns,
+            (
+                utils.get_dict_properties(
+                    app_cred,
+                    columns,
+                )
+            ),
+        )
diff --git a/openstackclient/tests/functional/identity/v3/test_application_credential.py b/openstackclient/tests/functional/identity/v3/test_application_credential.py
index 22f2b90bb4..9c8b0462ff 100644
--- a/openstackclient/tests/functional/identity/v3/test_application_credential.py
+++ b/openstackclient/tests/functional/identity/v3/test_application_credential.py
@@ -21,13 +21,13 @@ from openstackclient.tests.functional.identity.v3 import common
 
 class ApplicationCredentialTests(common.IdentityTests):
     APPLICATION_CREDENTIAL_FIELDS = [
-        'id',
-        'name',
-        'project_id',
-        'description',
-        'roles',
-        'expires_at',
-        'unrestricted',
+        'ID',
+        'Name',
+        'Project ID',
+        'Description',
+        'Roles',
+        'Expires At',
+        'Unrestricted',
     ]
     APPLICATION_CREDENTIAL_LIST_HEADERS = [
         'ID',
diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py
index d9c3531ddd..277204dd45 100644
--- a/openstackclient/tests/unit/identity/v3/test_application_credential.py
+++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py
@@ -13,37 +13,58 @@
 #   under the License.
 #
 
-import copy
-import json
+import datetime
 from unittest import mock
+from unittest.mock import call
 
 from osc_lib import exceptions
-from osc_lib import utils
 
+from openstack import exceptions as sdk_exceptions
+from openstack.identity.v3 import (
+    application_credential as _application_credential,
+)
+from openstack.identity.v3 import role as _role
+from openstack.test import fakes as sdk_fakes
 from openstackclient.identity.v3 import application_credential
-from openstackclient.tests.unit import fakes
 from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
 
 
-class TestApplicationCredential(identity_fakes.TestIdentityv3):
+class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3):
+    columns = (
+        'ID',
+        'Name',
+        'Description',
+        'Project ID',
+        'Roles',
+        'Unrestricted',
+        'Access Rules',
+        'Expires At',
+        'Secret',
+    )
+
     def setUp(self):
         super().setUp()
 
-        identity_manager = self.identity_client
-        self.app_creds_mock = identity_manager.application_credentials
-        self.app_creds_mock.reset_mock()
-        self.roles_mock = identity_manager.roles
-        self.roles_mock.reset_mock()
+        self.roles = sdk_fakes.generate_fake_resource(_role.Role)
+        self.application_credential = sdk_fakes.generate_fake_resource(
+            resource_type=_application_credential.ApplicationCredential,
+            roles=[],
+        )
 
+        self.datalist = (
+            self.application_credential.id,
+            self.application_credential.name,
+            self.application_credential.description,
+            self.application_credential.project_id,
+            self.application_credential.roles,
+            self.application_credential.unrestricted,
+            self.application_credential.access_rules,
+            self.application_credential.expires_at,
+            self.application_credential.secret,
+        )
 
-class TestApplicationCredentialCreate(TestApplicationCredential):
-    def setUp(self):
-        super().setUp()
-
-        self.roles_mock.get.return_value = fakes.FakeResource(
-            None,
-            copy.deepcopy(identity_fakes.ROLE),
-            loaded=True,
+        self.identity_sdk_client.create_application_credential.return_value = (
+            self.application_credential
         )
 
         # Get the command object to test
@@ -52,17 +73,14 @@ class TestApplicationCredentialCreate(TestApplicationCredential):
         )
 
     def test_application_credential_create_basic(self):
-        self.app_creds_mock.create.return_value = fakes.FakeResource(
-            None,
-            copy.deepcopy(identity_fakes.APP_CRED_BASIC),
-            loaded=True,
-        )
-
-        name = identity_fakes.app_cred_name
+        name = self.application_credential.name
         arglist = [name]
-        verifylist = [('name', identity_fakes.app_cred_name)]
+        verifylist = [('name', self.application_credential.name)]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
+        conn = self.app.client_manager.sdk_connection
+        user_id = conn.config.get_auth().get_user_id(conn.identity)
+
         # In base command class ShowOne in cliff, abstract method take_action()
         # returns a two-part tuple with a tuple of column names and a tuple of
         # data to be shown.
@@ -70,68 +88,45 @@ class TestApplicationCredentialCreate(TestApplicationCredential):
 
         # Set expected values
         kwargs = {
-            'secret': None,
             'roles': [],
             'expires_at': None,
             'description': None,
+            'secret': None,
             'unrestricted': False,
-            'access_rules': None,
+            'access_rules': [],
         }
-        self.app_creds_mock.create.assert_called_with(name, **kwargs)
+        self.identity_sdk_client.create_application_credential.assert_called_with(
+            user_id, name, **kwargs
+        )
 
-        collist = (
-            'access_rules',
-            'description',
-            'expires_at',
-            'id',
-            'name',
-            'project_id',
-            'roles',
-            'secret',
-            'unrestricted',
-        )
-        self.assertEqual(collist, columns)
-        datalist = (
-            None,
-            None,
-            None,
-            identity_fakes.app_cred_id,
-            identity_fakes.app_cred_name,
-            identity_fakes.project_id,
-            identity_fakes.role_name,
-            identity_fakes.app_cred_secret,
-            False,
-        )
-        self.assertEqual(datalist, data)
+        self.assertEqual(self.columns, columns)
+        self.assertEqual(self.datalist, data)
 
     def test_application_credential_create_with_options(self):
-        name = identity_fakes.app_cred_name
-        self.app_creds_mock.create.return_value = fakes.FakeResource(
-            None,
-            copy.deepcopy(identity_fakes.APP_CRED_OPTIONS),
-            loaded=True,
-        )
-
+        name = self.application_credential.name
         arglist = [
             name,
             '--secret',
             'moresecuresecret',
             '--role',
-            identity_fakes.role_id,
+            self.roles.id,
             '--expiration',
-            identity_fakes.app_cred_expires_str,
+            '2024-01-01T00:00:00',
             '--description',
             'credential for testing',
         ]
         verifylist = [
-            ('name', identity_fakes.app_cred_name),
+            ('name', self.application_credential.name),
             ('secret', 'moresecuresecret'),
-            ('role', [identity_fakes.role_id]),
-            ('expiration', identity_fakes.app_cred_expires_str),
+            ('role', [self.roles.id]),
+            ('expiration', '2024-01-01T00:00:00'),
             ('description', 'credential for testing'),
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
+        conn = self.app.client_manager.sdk_connection
+        user_id = conn.config.get_auth().get_user_id(conn.identity)
+
         # In base command class ShowOne in cliff, abstract method take_action()
         # returns a two-part tuple with a tuple of column names and a tuple of
         # data to be shown.
@@ -139,172 +134,119 @@ class TestApplicationCredentialCreate(TestApplicationCredential):
 
         # Set expected values
         kwargs = {
-            'secret': 'moresecuresecret',
-            'roles': [identity_fakes.role_id],
-            'expires_at': identity_fakes.app_cred_expires,
+            'roles': [{'id': self.roles.id}],
+            'expires_at': datetime.datetime(2024, 1, 1, 0, 0),
             'description': 'credential for testing',
+            'secret': 'moresecuresecret',
             'unrestricted': False,
-            'access_rules': None,
+            'access_rules': [],
         }
-        self.app_creds_mock.create.assert_called_with(name, **kwargs)
+        self.identity_sdk_client.create_application_credential.assert_called_with(
+            user_id, name, **kwargs
+        )
 
-        collist = (
-            'access_rules',
-            'description',
-            'expires_at',
-            'id',
-            'name',
-            'project_id',
-            'roles',
-            'secret',
-            'unrestricted',
-        )
-        self.assertEqual(collist, columns)
-        datalist = (
-            None,
-            identity_fakes.app_cred_description,
-            identity_fakes.app_cred_expires_str,
-            identity_fakes.app_cred_id,
-            identity_fakes.app_cred_name,
-            identity_fakes.project_id,
-            identity_fakes.role_name,
-            identity_fakes.app_cred_secret,
-            False,
-        )
-        self.assertEqual(datalist, data)
+        self.assertEqual(self.columns, columns)
+        self.assertEqual(self.datalist, data)
 
     def test_application_credential_create_with_access_rules_string(self):
-        name = identity_fakes.app_cred_name
-        self.app_creds_mock.create.return_value = fakes.FakeResource(
-            None,
-            copy.deepcopy(identity_fakes.APP_CRED_ACCESS_RULES),
-            loaded=True,
-        )
+        name = self.application_credential.name
 
         arglist = [
             name,
             '--access-rules',
-            identity_fakes.app_cred_access_rules,
+            '[{"path": "/v2.1/servers", "method": "GET", "service": "compute"}]',
         ]
         verifylist = [
-            ('name', identity_fakes.app_cred_name),
-            ('access_rules', identity_fakes.app_cred_access_rules),
+            ('name', self.application_credential.name),
+            (
+                'access_rules',
+                '[{"path": "/v2.1/servers", "method": "GET", "service": "compute"}]',
+            ),
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
+        conn = self.app.client_manager.sdk_connection
+        user_id = conn.config.get_auth().get_user_id(conn.identity)
+
         columns, data = self.cmd.take_action(parsed_args)
 
         # Set expected values
         kwargs = {
-            'secret': None,
             'roles': [],
             'expires_at': None,
             'description': None,
+            'secret': None,
             'unrestricted': False,
-            'access_rules': json.loads(identity_fakes.app_cred_access_rules),
+            'access_rules': [
+                {
+                    "path": "/v2.1/servers",
+                    "method": "GET",
+                    "service": "compute",
+                }
+            ],
         }
-        self.app_creds_mock.create.assert_called_with(name, **kwargs)
+        self.identity_sdk_client.create_application_credential.assert_called_with(
+            user_id, name, **kwargs
+        )
 
-        collist = (
-            'access_rules',
-            'description',
-            'expires_at',
-            'id',
-            'name',
-            'project_id',
-            'roles',
-            'secret',
-            'unrestricted',
-        )
-        self.assertEqual(collist, columns)
-        datalist = (
-            identity_fakes.app_cred_access_rules,
-            None,
-            None,
-            identity_fakes.app_cred_id,
-            identity_fakes.app_cred_name,
-            identity_fakes.project_id,
-            identity_fakes.role_name,
-            identity_fakes.app_cred_secret,
-            False,
-        )
-        self.assertEqual(datalist, data)
+        self.assertEqual(self.columns, columns)
+        self.assertEqual(self.datalist, data)
 
     @mock.patch('openstackclient.identity.v3.application_credential.json.load')
     @mock.patch('openstackclient.identity.v3.application_credential.open')
     def test_application_credential_create_with_access_rules_file(
         self, _, mock_json_load
     ):
-        mock_json_load.return_value = identity_fakes.app_cred_access_rules
-
-        name = identity_fakes.app_cred_name
-        self.app_creds_mock.create.return_value = fakes.FakeResource(
-            None,
-            copy.deepcopy(identity_fakes.APP_CRED_ACCESS_RULES),
-            loaded=True,
-        )
+        mock_json_load.return_value = '/tmp/access_rules.json'
+        name = self.application_credential.name
 
         arglist = [
             name,
             '--access-rules',
-            identity_fakes.app_cred_access_rules_path,
+            '/tmp/access_rules.json',
         ]
         verifylist = [
-            ('name', identity_fakes.app_cred_name),
-            ('access_rules', identity_fakes.app_cred_access_rules_path),
+            ('name', self.application_credential.name),
+            ('access_rules', '/tmp/access_rules.json'),
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
+        conn = self.app.client_manager.sdk_connection
+        user_id = conn.config.get_auth().get_user_id(conn.identity)
+
         columns, data = self.cmd.take_action(parsed_args)
 
         # Set expected values
         kwargs = {
-            'secret': None,
             'roles': [],
             'expires_at': None,
             'description': None,
+            'secret': None,
             'unrestricted': False,
-            'access_rules': identity_fakes.app_cred_access_rules,
+            'access_rules': '/tmp/access_rules.json',
         }
-        self.app_creds_mock.create.assert_called_with(name, **kwargs)
-
-        collist = (
-            'access_rules',
-            'description',
-            'expires_at',
-            'id',
-            'name',
-            'project_id',
-            'roles',
-            'secret',
-            'unrestricted',
+        self.identity_sdk_client.create_application_credential.assert_called_with(
+            user_id, name, **kwargs
         )
-        self.assertEqual(collist, columns)
-        datalist = (
-            identity_fakes.app_cred_access_rules,
-            None,
-            None,
-            identity_fakes.app_cred_id,
-            identity_fakes.app_cred_name,
-            identity_fakes.project_id,
-            identity_fakes.role_name,
-            identity_fakes.app_cred_secret,
-            False,
-        )
-        self.assertEqual(datalist, data)
+
+        self.assertEqual(self.columns, columns)
+        self.assertEqual(self.datalist, data)
 
 
-class TestApplicationCredentialDelete(TestApplicationCredential):
+class TestApplicationCredentialDelete(identity_fakes.TestIdentityv3):
     def setUp(self):
         super().setUp()
 
-        # This is the return value for utils.find_resource()
-        self.app_creds_mock.get.return_value = fakes.FakeResource(
-            None,
-            copy.deepcopy(identity_fakes.APP_CRED_BASIC),
-            loaded=True,
+        self.application_credential = sdk_fakes.generate_fake_resource(
+            resource_type=_application_credential.ApplicationCredential,
+            roles=[],
+        )
+        self.identity_sdk_client.find_application_credential.return_value = (
+            self.application_credential
+        )
+        self.identity_sdk_client.delete_application_credential.return_value = (
+            None
         )
-        self.app_creds_mock.delete.return_value = None
 
         # Get the command object to test
         self.cmd = application_credential.DeleteApplicationCredential(
@@ -313,26 +255,31 @@ class TestApplicationCredentialDelete(TestApplicationCredential):
 
     def test_application_credential_delete(self):
         arglist = [
-            identity_fakes.app_cred_id,
+            self.application_credential.id,
+        ]
+        verifylist = [
+            ('application_credential', [self.application_credential.id])
         ]
-        verifylist = [('application_credential', [identity_fakes.app_cred_id])]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
+        conn = self.app.client_manager.sdk_connection
+        user_id = conn.config.get_auth().get_user_id(conn.identity)
+
         result = self.cmd.take_action(parsed_args)
 
-        self.app_creds_mock.delete.assert_called_with(
-            identity_fakes.app_cred_id,
+        self.identity_sdk_client.delete_application_credential.assert_called_with(
+            user_id,
+            self.application_credential.id,
         )
         self.assertIsNone(result)
 
-    @mock.patch.object(utils, 'find_resource')
-    def test_delete_multi_app_creds_with_exception(self, find_mock):
-        find_mock.side_effect = [
-            self.app_creds_mock.get.return_value,
-            exceptions.CommandError,
+    def test_delete_multi_app_creds_with_exception(self):
+        self.identity_sdk_client.find_application_credential.side_effect = [
+            self.application_credential,
+            sdk_exceptions.NotFoundException,
         ]
         arglist = [
-            identity_fakes.app_cred_id,
+            self.application_credential.id,
             'nonexistent_app_cred',
         ]
         verifylist = [
@@ -340,6 +287,9 @@ class TestApplicationCredentialDelete(TestApplicationCredential):
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
+        conn = self.app.client_manager.sdk_connection
+        user_id = conn.config.get_auth().get_user_id(conn.identity)
+
         try:
             self.cmd.take_action(parsed_args)
             self.fail('CommandError should be raised.')
@@ -348,27 +298,32 @@ class TestApplicationCredentialDelete(TestApplicationCredential):
                 '1 of 2 application credentials failed to' ' delete.', str(e)
             )
 
-        find_mock.assert_any_call(
-            self.app_creds_mock, identity_fakes.app_cred_id
-        )
-        find_mock.assert_any_call(self.app_creds_mock, 'nonexistent_app_cred')
+        calls = []
+        for a in arglist:
+            calls.append(call(user_id, a))
 
-        self.assertEqual(2, find_mock.call_count)
-        self.app_creds_mock.delete.assert_called_once_with(
-            identity_fakes.app_cred_id
+        self.identity_sdk_client.find_application_credential.assert_has_calls(
+            calls
+        )
+
+        self.assertEqual(
+            2, self.identity_sdk_client.find_application_credential.call_count
+        )
+        self.identity_sdk_client.delete_application_credential.assert_called_once_with(
+            user_id, self.application_credential.id
         )
 
 
-class TestApplicationCredentialList(TestApplicationCredential):
+class TestApplicationCredentialList(identity_fakes.TestIdentityv3):
     def setUp(self):
         super().setUp()
 
-        self.app_creds_mock.list.return_value = [
-            fakes.FakeResource(
-                None,
-                copy.deepcopy(identity_fakes.APP_CRED_BASIC),
-                loaded=True,
-            ),
+        self.application_credential = sdk_fakes.generate_fake_resource(
+            resource_type=_application_credential.ApplicationCredential,
+            roles=[],
+        )
+        self.identity_sdk_client.application_credentials.return_value = [
+            self.application_credential
         ]
 
         # Get the command object to test
@@ -381,35 +336,54 @@ class TestApplicationCredentialList(TestApplicationCredential):
         verifylist = []
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
+        conn = self.app.client_manager.sdk_connection
+        user_id = conn.config.get_auth().get_user_id(conn.identity)
+
         # In base command class Lister in cliff, abstract method take_action()
         # returns a tuple containing the column names and an iterable
         # containing the data to be listed.
         columns, data = self.cmd.take_action(parsed_args)
 
-        self.app_creds_mock.list.assert_called_with(user=None)
+        self.identity_sdk_client.application_credentials.assert_called_with(
+            user=user_id
+        )
 
-        collist = ('ID', 'Name', 'Project ID', 'Description', 'Expires At')
+        collist = (
+            'ID',
+            'Name',
+            'Description',
+            'Project ID',
+            'Roles',
+            'Unrestricted',
+            'Access Rules',
+            'Expires At',
+        )
         self.assertEqual(collist, columns)
         datalist = (
             (
-                identity_fakes.app_cred_id,
-                identity_fakes.app_cred_name,
-                identity_fakes.project_id,
-                None,
-                None,
+                self.application_credential.id,
+                self.application_credential.name,
+                self.application_credential.description,
+                self.application_credential.project_id,
+                self.application_credential.roles,
+                self.application_credential.unrestricted,
+                self.application_credential.access_rules,
+                self.application_credential.expires_at,
             ),
         )
         self.assertEqual(datalist, tuple(data))
 
 
-class TestApplicationCredentialShow(TestApplicationCredential):
+class TestApplicationCredentialShow(identity_fakes.TestIdentityv3):
     def setUp(self):
         super().setUp()
 
-        self.app_creds_mock.get.return_value = fakes.FakeResource(
-            None,
-            copy.deepcopy(identity_fakes.APP_CRED_BASIC),
-            loaded=True,
+        self.application_credential = sdk_fakes.generate_fake_resource(
+            resource_type=_application_credential.ApplicationCredential,
+            roles=[],
+        )
+        self.identity_sdk_client.find_application_credential.return_value = (
+            self.application_credential
         )
 
         # Get the command object to test
@@ -419,41 +393,44 @@ class TestApplicationCredentialShow(TestApplicationCredential):
 
     def test_application_credential_show(self):
         arglist = [
-            identity_fakes.app_cred_id,
+            self.application_credential.id,
         ]
         verifylist = [
-            ('application_credential', identity_fakes.app_cred_id),
+            ('application_credential', self.application_credential.id),
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
+        conn = self.app.client_manager.sdk_connection
+        user_id = conn.config.get_auth().get_user_id(conn.identity)
+
         # In base command class ShowOne in cliff, abstract method take_action()
         # returns a two-part tuple with a tuple of column names and a tuple of
         # data to be shown.
         columns, data = self.cmd.take_action(parsed_args)
 
-        self.app_creds_mock.get.assert_called_with(identity_fakes.app_cred_id)
+        self.identity_sdk_client.find_application_credential.assert_called_with(
+            user_id, self.application_credential.id
+        )
 
         collist = (
-            'access_rules',
-            'description',
-            'expires_at',
-            'id',
-            'name',
-            'project_id',
-            'roles',
-            'secret',
-            'unrestricted',
+            'ID',
+            'Name',
+            'Description',
+            'Project ID',
+            'Roles',
+            'Unrestricted',
+            'Access Rules',
+            'Expires At',
         )
         self.assertEqual(collist, columns)
         datalist = (
-            None,
-            None,
-            None,
-            identity_fakes.app_cred_id,
-            identity_fakes.app_cred_name,
-            identity_fakes.project_id,
-            identity_fakes.role_name,
-            identity_fakes.app_cred_secret,
-            False,
+            self.application_credential.id,
+            self.application_credential.name,
+            self.application_credential.description,
+            self.application_credential.project_id,
+            self.application_credential.roles,
+            self.application_credential.unrestricted,
+            self.application_credential.access_rules,
+            self.application_credential.expires_at,
         )
         self.assertEqual(datalist, data)
diff --git a/releasenotes/notes/migrate-application-credential-to-sdk-c79d8dfc3c8e1d9f.yaml b/releasenotes/notes/migrate-application-credential-to-sdk-c79d8dfc3c8e1d9f.yaml
new file mode 100644
index 0000000000..22b7a2971f
--- /dev/null
+++ b/releasenotes/notes/migrate-application-credential-to-sdk-c79d8dfc3c8e1d9f.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    The following commands have been migrated to SDK:
+
+    - ``application credential create``
+    - ``application credential delete``
+    - ``application credential list``
+    - ``application credential show``