Skip to content

Commit e9d768c

Browse files
committed
refactor and simplify CardFactory, update tests and docstrings
1 parent 4b9c5c0 commit e9d768c

File tree

4 files changed

+226
-475
lines changed

4 files changed

+226
-475
lines changed
Lines changed: 62 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33

4-
import warnings
5-
from typing import Any, List, Union
6-
from botbuilder.schema import (ActionTypes, AnimationCard, Attachment, AudioCard,
7-
CardAction, CardImage, Fact, HeroCard,
8-
MediaUrl, OAuthCard, ReceiptCard,
9-
ReceiptItem, SigninCard, ThumbnailCard, VideoCard)
4+
from botbuilder.schema import (AnimationCard, Attachment, AudioCard,
5+
HeroCard, OAuthCard, ReceiptCard,
6+
SigninCard, ThumbnailCard, VideoCard)
107

118

129
class ContentTypes:
@@ -25,263 +22,137 @@ class CardFactory:
2522
content_types = ContentTypes
2623

2724
@staticmethod
28-
def adaptive_card(card: Any) -> Attachment:
25+
def adaptive_card(card: dict) -> Attachment:
2926
"""
3027
Returns an attachment for an adaptive card. The attachment will contain the card and the
31-
appropriate 'contentType'.
28+
appropriate 'contentType'. Will raise a TypeError if the 'card' argument is not an
29+
dict.
3230
:param card:
3331
:return:
3432
"""
33+
if not type(card) == dict:
34+
raise TypeError('CardFactory.adaptive_card(): `card` argument is not of type dict, unable to prepare '
35+
'attachment.')
36+
3537
return Attachment(content_type=CardFactory.content_types.adaptive_card,
3638
content=card)
3739

3840
@staticmethod
39-
def animation_card(title: str, media: List[Union[MediaUrl, str]],
40-
buttons: List[Union[CardAction, str]] = None, other: AnimationCard = None) -> Attachment:
41+
def animation_card(card: AnimationCard) -> Attachment:
4142
"""
42-
Returns an attachment for an animation card. Will emit an error if the `other` parameter is not an
43+
Returns an attachment for an animation card. Will raise a TypeError if the 'card' argument is not an
4344
AnimationCard.
44-
:param title:
45-
:param media:
46-
:param buttons:
47-
:param other:
45+
:param card:
4846
:return:
4947
"""
50-
card = AnimationCard()
51-
if isinstance(other, AnimationCard):
52-
card = other
53-
elif other is not None:
54-
warnings.warn('CardFactory.animation_card(): `other` parameter is not an instance of an AnimationCard, '
55-
'ignoring `other` in card generation.')
56-
if title:
57-
card.title = title
58-
if media:
59-
card.media = CardFactory.media(media)
60-
if buttons:
61-
card.buttons = CardFactory.actions(buttons)
48+
if not isinstance(card, AnimationCard):
49+
raise TypeError('CardFactory.animation_card(): `card` argument is not an instance of an AnimationCard, '
50+
'unable to prepare attachment.')
6251

6352
return Attachment(content_type=CardFactory.content_types.animation_card,
6453
content=card)
6554

6655
@staticmethod
67-
def audio_card(title: str, media: List[Union[str, MediaUrl]],
68-
buttons: List[Union[str, CardAction]] = None, other: AudioCard = None) -> Attachment:
69-
card = AudioCard()
70-
if isinstance(other, AudioCard):
71-
card = other
72-
elif other is not None:
73-
warnings.warn('CardFactory.audio_card(): `other` parameter is not an instance of an AudioCard, ignoring '
74-
'`other` in card generation.')
75-
if title:
76-
card.title = title
77-
if media:
78-
card.media = CardFactory.media(media)
79-
if buttons:
80-
card.buttons = CardFactory.actions(buttons)
56+
def audio_card(card: AudioCard) -> Attachment:
57+
"""
58+
Returns an attachment for an audio card. Will raise a TypeError if 'card' argument is not an AudioCard.
59+
:param card:
60+
:return:
61+
"""
62+
if not isinstance(card, AudioCard):
63+
raise TypeError('CardFactory.audio_card(): `card` argument is not an instance of an AudioCard, '
64+
'unable to prepare attachment.')
8165

8266
return Attachment(content_type=CardFactory.content_types.audio_card,
8367
content=card)
8468

8569
@staticmethod
86-
def hero_card(title: str, text: str = None, images: List[Union[str, CardImage]] = None,
87-
buttons: List[Union[str, CardImage]] = None, other: HeroCard = None) -> Attachment:
70+
def hero_card(card: HeroCard) -> Attachment:
8871
"""
89-
Returns an attachment for a hero card.
72+
Returns an attachment for a hero card. Will raise a TypeError if 'card' argument is not a HeroCard.
9073
9174
Hero cards tend to have one dominant full width image and the cards text & buttons can
9275
usually be found below the image.
9376
:return:
9477
"""
95-
card = HeroCard()
96-
if isinstance(other, HeroCard):
97-
card = other
98-
elif other is not None:
99-
warnings.warn('CardFactory.hero_card(): `other` parameter is not an instance of an HeroCard, '
100-
'ignoring `other` in card generation.')
101-
if title:
102-
card.title = title
103-
if text:
104-
card.text = text
105-
if images:
106-
card.images = CardFactory.images(images)
107-
if buttons:
108-
card.buttons = CardFactory.actions(buttons)
78+
if not isinstance(card, HeroCard):
79+
raise TypeError('CardFactory.hero_card(): `card` argument is not an instance of an HeroCard, '
80+
'unable to prepare attachment.')
10981

11082
return Attachment(content_type=CardFactory.content_types.hero_card,
11183
content=card)
11284

11385
@staticmethod
114-
def oauth_card(connection_name, title: str, text: str = None) -> Attachment:
86+
def oauth_card(card: OAuthCard) -> Attachment:
11587
"""
116-
Returns an attachment for an OAuth card used by the Bot Frameworks Single Sign On (SSO) service.
117-
:param connection_name:
118-
:param title:
119-
:param text:
88+
Returns an attachment for an OAuth card used by the Bot Frameworks Single Sign On (SSO) service. Will raise a
89+
TypeError if 'card' argument is not a OAuthCard.
90+
:param card:
12091
:return:
12192
"""
122-
button = CardAction(type=ActionTypes.signin, title=title, value=None)
123-
card = OAuthCard(connection_name=connection_name, buttons=[button], text=text)
93+
if not isinstance(card, OAuthCard):
94+
raise TypeError('CardFactory.oauth_card(): `card` argument is not an instance of an OAuthCard, '
95+
'unable to prepare attachment.')
12496

12597
return Attachment(content_type=CardFactory.content_types.oauth_card,
12698
content=card)
12799

128100
@staticmethod
129-
def receipt_card(title: str = None, facts: List[Fact] = None, items: List[ReceiptItem] = None,
130-
tap: CardAction = None,
131-
total: str = None, tax: str = None, vat: str = None,
132-
buttons: List[CardAction] = None, other: ReceiptCard = None) -> Attachment:
101+
def receipt_card(card: ReceiptCard) -> Attachment:
133102
"""
134-
Returns an attachment for a receipt card. The attachment will contain the parameters and the appropriate
135-
`contentType`.
136-
:param title:
137-
:param facts:
138-
:param items:
139-
:param tap:
140-
:param total:
141-
:param tax:
142-
:param vat:
143-
:param buttons:
144-
:param other:
103+
Returns an attachment for a receipt card. Will raise a TypeError if 'card' argument is not a ReceiptCard.
104+
:param card:
145105
:return:
146106
"""
147-
card = ReceiptCard()
148-
if isinstance(other, ReceiptCard):
149-
card = other
150-
elif other is not None:
151-
warnings.warn('CardFactory.receipt_card(): `other` parameter is not an instance of an ReceiptCard, '
152-
'ignoring `other` in card generation.')
153-
154-
if title is not None:
155-
card.title = title
156-
if facts is not None:
157-
card.facts = facts
158-
if items is not None:
159-
card.items = items
160-
if tap is not None:
161-
card.tap = tap
162-
if total is not None:
163-
card.total = total
164-
if tax is not None:
165-
card.tax = tax
166-
if vat is not None:
167-
card.vat = vat
168-
if buttons is not None:
169-
card.buttons = buttons
107+
if not isinstance(card, ReceiptCard):
108+
raise TypeError('CardFactory.receipt_card(): `card` argument is not an instance of an ReceiptCard, '
109+
'unable to prepare attachment.')
170110

171111
return Attachment(content_type=CardFactory.content_types.receipt_card,
172112
content=card)
173113

174114
@staticmethod
175-
def signin_card(title: str, url: str, text: str = None) -> Attachment:
115+
def signin_card(card: SigninCard) -> Attachment:
176116
"""
177117
Returns an attachment for a signin card. For channels that don't natively support signin cards an alternative
178-
message will be rendered.
179-
:param title:
180-
:param url:
181-
:param text:
118+
message will be rendered. Will raise a TypeError if 'card' argument is not a SigninCard.
119+
:param card:
182120
:return:
183121
"""
184-
button = CardAction(type=ActionTypes.signin, title=title, value=url)
185-
card = SigninCard(text=text, buttons=[button])
122+
if not isinstance(card, SigninCard):
123+
raise TypeError('CardFactory.signin_card(): `card` argument is not an instance of an SigninCard, '
124+
'unable to prepare attachment.')
186125

187126
return Attachment(content_type=CardFactory.content_types.signin_card,
188127
content=card)
189128

190129
@staticmethod
191-
def thumbnail_card(title: str, text: str = None, images: List[Union[str, CardImage]] = None,
192-
buttons: List[Union[CardAction, str]] = None, other: ThumbnailCard = None) -> Attachment:
130+
def thumbnail_card(card: ThumbnailCard) -> Attachment:
193131
"""
194-
Returns an attachment for a thumbnail card. Thumbnail cards are similar to [hero cards](#herocard)
132+
Returns an attachment for a thumbnail card. Thumbnail cards are similar to
195133
but instead of a full width image, they're typically rendered with a smaller thumbnail version of
196134
the image on either side and the text will be rendered in column next to the image. Any buttons
197-
will typically show up under the card.
198-
:param title:
199-
:param text:
200-
:param images:
201-
:param buttons:
202-
:param other:
135+
will typically show up under the card. Will raise a TypeError if 'card' argument is not a ThumbnailCard.
136+
:param card:
203137
:return:
204138
"""
205-
card = ThumbnailCard()
206-
if isinstance(other, ThumbnailCard):
207-
card = other
208-
elif other is not None:
209-
warnings.warn('CardFactory.thumbnail_card(): `other` parameter is not an instance of an ThumbnailCard, '
210-
'ignoring `other` in card generation.')
211-
if title:
212-
card.title = title
213-
if text:
214-
card.text = text
215-
if images:
216-
card.images = CardFactory.images(images)
217-
if buttons:
218-
card.buttons = CardFactory.actions(buttons)
139+
if not isinstance(card, ThumbnailCard):
140+
raise TypeError('CardFactory.thumbnail_card(): `card` argument is not an instance of an ThumbnailCard, '
141+
'unable to prepare attachment.')
219142

220143
return Attachment(content_type=CardFactory.content_types.thumbnail_card,
221144
content=card)
222145

223146
@staticmethod
224-
def video_card(title: str, media: List[Union[str, MediaUrl]],
225-
buttons: List[Union[CardAction, str]] = None, other: VideoCard = None) -> Attachment:
147+
def video_card(card: VideoCard) -> Attachment:
226148
"""
227-
Returns an attachment for a video card.
228-
:param title:
229-
:param media:
230-
:param buttons:
231-
:param other:
149+
Returns an attachment for a video card. Will raise a TypeError if 'card' argument is not a VideoCard.
150+
:param card:
232151
:return:
233152
"""
234-
card = VideoCard()
235-
if isinstance(other, VideoCard):
236-
card = other
237-
elif other is not None:
238-
warnings.warn('CardFactory.video_card(): `other` parameter is not an instance of an VideoCard, '
239-
'ignoring `other` in card generation.')
240-
if title:
241-
card.title = title
242-
if media:
243-
card.media = CardFactory.media(media)
244-
if buttons:
245-
card.buttons = CardFactory.actions(buttons)
153+
if not isinstance(card, VideoCard):
154+
raise TypeError('CardFactory.video_card(): `card` argument is not an instance of an VideoCard, '
155+
'unable to prepare attachment.')
246156

247157
return Attachment(content_type=CardFactory.content_types.video_card,
248158
content=card)
249-
250-
@staticmethod
251-
def actions(actions: List[Union[CardAction, str]]) -> List[CardAction]:
252-
if actions is None:
253-
return []
254-
255-
def prepare_action(action_or_str):
256-
if isinstance(action_or_str, CardAction):
257-
return action_or_str
258-
else:
259-
return CardAction(type=ActionTypes.im_back, value=str(action_or_str), title=str(action_or_str))
260-
261-
return [prepare_action(action) for action in actions]
262-
263-
@staticmethod
264-
def images(images: List[Union[CardImage, str]]) -> List[CardImage]:
265-
if images is None:
266-
return []
267-
268-
def prepare_image(image_or_str):
269-
if isinstance(image_or_str, CardImage):
270-
return image_or_str
271-
else:
272-
return CardImage(url=image_or_str)
273-
274-
return [prepare_image(image) for image in images]
275-
276-
@staticmethod
277-
def media(links: List[Union[MediaUrl, str]]) -> List[MediaUrl]:
278-
if links is None:
279-
return []
280-
281-
def prepare_media(media_or_str):
282-
if isinstance(media_or_str, MediaUrl):
283-
return media_or_str
284-
else:
285-
return MediaUrl(url=media_or_str)
286-
287-
return [prepare_media(link) for link in links]

0 commit comments

Comments
 (0)