Contentstack 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
contentstack/stack.py ADDED
@@ -0,0 +1,760 @@
1
+ from contentstack import HTTPConnection
2
+ from contentstack import Config
3
+ from contentstack.errors import StackException
4
+
5
+
6
+ """
7
+
8
+ Created by Shailesh Mishra on 22/06/19.
9
+ Copyright 2019 Contentstack. All rights reserved.
10
+
11
+ contentstack.stack
12
+ ~~~~~~~~~~~~~~~~~~
13
+
14
+ API Reference: https://www.contentstack.com/docs/apis/content-delivery-api/#stack
15
+
16
+ """
17
+
18
+
19
+ class Stack(object):
20
+
21
+ def __init__(self, **kwargs):
22
+
23
+ """
24
+ Argument kwargs is key-worded, variable-length argument list.
25
+ To initialise stack following fields should be provided.
26
+
27
+ api_key -> stack 'api_key' of your target stack, type of api_key should be str.
28
+ access_token -> stack 'access_token' of your target stack, type of access_token should be str.
29
+ environment -> stack 'environment' of your target stack, type of environment should be str
30
+ config: config='<contentstack.config.Config>' (optional) stack configuration to setup custom API.
31
+
32
+ ==============================
33
+
34
+ [Example]:
35
+
36
+ >>> import contentstack
37
+ >>> stack: Stack = Stack(api_key ='api_key', access_token='access_token', environment='environment')
38
+
39
+ To declare stack with custom configuration
40
+
41
+ >>> config = Config()
42
+ >>> config.host('cdn.contentstack.io')
43
+ >>> stack = Stack(api_key ='API_Key',access_token='access_token',environment='environment', config=config)
44
+
45
+ ==============================
46
+
47
+ """
48
+
49
+ self.config = None
50
+ self.__http_request = None
51
+ self.__query_params = dict()
52
+ self.__stack_headers = dict()
53
+ self.__headers = dict()
54
+ self.__image_transform_url = None
55
+ self.__image_params = dict
56
+ self.__sync_query = dict()
57
+ for key, value in kwargs.items():
58
+ self.__headers[key] = value
59
+ self.__initialise_stack()
60
+
61
+ @property
62
+ def api_key(self):
63
+
64
+ """Returns api_KEY of the stack
65
+
66
+ Returns:
67
+ str -- api_key of the stack
68
+
69
+ ==============================
70
+
71
+ [Example]:
72
+
73
+ >>> from contentstack import Stack
74
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
75
+ >>> api_key = stack.api_key
76
+
77
+ ==============================
78
+
79
+ """
80
+
81
+ if 'api_key' in self.__stack_headers:
82
+ app_key = self.__stack_headers['api_key']
83
+ return app_key
84
+
85
+ @property
86
+ def get_http_instance(self):
87
+
88
+ """This method returns http_instance of the stack.
89
+
90
+ Returns:
91
+ str -- instance of the HttpRequest
92
+
93
+ ==============================
94
+
95
+ [Example]:
96
+
97
+ >>> from contentstack import Stack
98
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
99
+ >>> http_instance = stack.get_http_instance
100
+
101
+ ==============================
102
+
103
+ """
104
+
105
+ return self.__http_request
106
+
107
+ @property
108
+ def access_token(self):
109
+
110
+ """access_token property returns access_token of the stack
111
+
112
+ Returns:
113
+ str -- access_token of the stack
114
+
115
+ ==============================
116
+
117
+ [Example]:
118
+
119
+ >>> from contentstack import Stack
120
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
121
+ >>> access_token = stack.access_token
122
+
123
+ ==============================
124
+
125
+ """
126
+
127
+ if 'access_token' in self.__stack_headers:
128
+ access_token = self.__stack_headers['access_token']
129
+ return access_token
130
+ else:
131
+ return None
132
+
133
+ @property
134
+ def environment(self):
135
+
136
+ """environment property returns environment of the stack
137
+
138
+ Returns:
139
+ str -- environment of the stack
140
+
141
+ ==============================
142
+
143
+ [Example]:
144
+
145
+ >>> from contentstack import Stack
146
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
147
+ >>> environment = stack.environment
148
+
149
+ ==============================
150
+
151
+ """
152
+
153
+ if 'environment' in self.__stack_headers:
154
+ return self.__stack_headers['environment']
155
+ else:
156
+ return None
157
+
158
+ @environment.setter
159
+ def environment(self, environment):
160
+
161
+ """
162
+ environment property helps to set environment of the stack
163
+
164
+ Arguments:
165
+ environment {str} -- environment of the stack
166
+
167
+ Raises:
168
+ KeyError: If environment is None, empty or not str type
169
+
170
+ ==============================
171
+
172
+ [Example]:
173
+
174
+ >>> from contentstack import Stack
175
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
176
+ >>> stack.environment = 'product'
177
+
178
+ ==============================
179
+
180
+ """
181
+
182
+ if environment is None:
183
+ raise KeyError
184
+ elif isinstance(environment, str):
185
+ self.__stack_headers['environment'] = environment
186
+ else:
187
+ raise KeyError('Argument environment should be str type')
188
+
189
+ @property
190
+ def headers(self):
191
+
192
+ """dictionary of the stack headers
193
+
194
+ Returns:
195
+ dict -- dictionary of the headers
196
+
197
+ ==============================
198
+
199
+ [Example]:
200
+
201
+ >>> from contentstack import Stack
202
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
203
+ >>> headers = stack.headers
204
+
205
+ ==============================
206
+
207
+ """
208
+
209
+ return self.__stack_headers
210
+
211
+ def __initialise_stack(self):
212
+ # __initialise_stack is the protected member of the asset, So outsiders can not access this file.
213
+ if len(self.__headers) > 0:
214
+ if 'api_key' not in self.__headers:
215
+ raise StackException('Kindly provide API_Key')
216
+ else:
217
+ self.__stack_headers['api_key'] = self.__headers['api_key']
218
+ if 'access_token' not in self.__headers:
219
+ raise StackException('Kindly provide access token')
220
+ else:
221
+ self.__stack_headers['access_token'] = self.__headers['access_token']
222
+ if 'environment' not in self.__headers:
223
+ raise StackException('Kindly provide environment')
224
+ else:
225
+ self.__stack_headers['environment'] = self.__headers['environment']
226
+ if 'config' in self.__headers:
227
+ self.config = self.__headers['config']
228
+ self.__headers.pop('config', None)
229
+ else:
230
+ self.config = Config()
231
+ self.__http_request = HTTPConnection(self.config.endpoint, self.__query_params, self.__stack_headers)
232
+
233
+ def content_type(self, content_type_id):
234
+
235
+ """
236
+ Gets ContentType of the Stack Read more about Content Types.
237
+ API Reference: https://www.contentstack.com/docs/apis/content-delivery-api/#content-types
238
+
239
+ Arguments:
240
+ content_type_id {str} -- content_type_id of entry
241
+
242
+ Raises:
243
+ StackException: If content_type_id is None, empty or not str type
244
+
245
+ Returns:
246
+ <contentstack.content_type.ContentType> -- Instance of the ContentType
247
+
248
+ ==============================
249
+
250
+ [Example]:
251
+
252
+ >>> from contentstack import Stack
253
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
254
+ >>> content_type = stack.content_type('product')
255
+
256
+ ==============================
257
+ """
258
+
259
+ from contentstack import ContentType
260
+ if content_type_id is not None and isinstance(content_type_id, str) and len(content_type_id) > 0:
261
+ content_type = ContentType(content_type_id)
262
+ content_type._instance(self)
263
+ return content_type
264
+ else:
265
+ raise StackException('Kindly provide valid content_type')
266
+
267
+ def get_content_types(self, query_params):
268
+
269
+ """
270
+ Fetches all Content Types from the Stack. This call returns comprehensive information
271
+ of all the content types available in a particular stack in your account.
272
+ API Reference: https://www.contentstack.com/docs/apis/content-delivery-api/#content-types
273
+
274
+ Arguments:
275
+ query_params {dict} -- query parameters for the content_types
276
+
277
+ Raises:
278
+ StackException: If stack instance is not found
279
+
280
+ Returns:
281
+ dict -- returns list of <contentstack.content_type.ContentType>
282
+
283
+ ==============================
284
+
285
+ [Example]:
286
+
287
+ >>> from contentstack import Stack
288
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
289
+ >>> content_types = stack.get_content_types()
290
+
291
+ ==============================
292
+ """
293
+ content_type_params: dict = {}
294
+ if self.config is None:
295
+ raise StackException('Kindly initialise stack')
296
+ endpoint = self.config.endpoint
297
+ url = '{}/content_types'.format(endpoint)
298
+ if query_params is not None and isinstance(query_params, dict):
299
+ content_type_params = query_params.copy()
300
+ result = self.__http_request.get_result(url, content_type_params, self.__stack_headers)
301
+
302
+ return result
303
+
304
+ def asset(self, uid=None):
305
+
306
+ """
307
+ Assets refer to all the media files (images, videos, PDFs, audio files, and so on)
308
+ uploaded in your Contentstack repository for future use. These files can be
309
+ attached and used in multiple entries. Learn more about Assets.
310
+ Keep uid None to fetch list of all assets
311
+ API Reference : https://www.contentstack.com/docs/guide/content-management#working-with-assets
312
+
313
+ Keyword Arguments:
314
+ uid {str} -- uid of the asset (default: {None})
315
+
316
+ Returns:
317
+ <contentstack.asset.Asset> -- class object of asset so we can chain the asset functions.
318
+
319
+ ==============================
320
+
321
+ [Example]: [Asset]
322
+ This call fetches the latest version of a specific asset of a particular stack.
323
+ provide asset_uid of the asset you have to find.
324
+ Example: in case to fetch single asset, provide uid of the asset:
325
+
326
+ >>> from contentstack import Stack
327
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
328
+ >>> asset_instance = stack.asset('bltputyourassetuid')
329
+ >>> asset = asset_instance.fetch()
330
+
331
+ ###############################
332
+
333
+ [Example]: [All Assets]
334
+
335
+ >>> from contentstack import Stack
336
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
337
+ >>> asset_instance = stack.asset()
338
+ >>> assets = asset_instance.fetch_all()
339
+
340
+ ==============================
341
+
342
+ """
343
+
344
+ from contentstack import Asset
345
+ asset = Asset(uid=uid)
346
+ asset._instance(self)
347
+ return asset
348
+
349
+ def image_transform(self, image_url: str, **kwargs):
350
+
351
+ """
352
+ The Image Delivery API is used to retrieve, manipulate and/or convert image
353
+ files of your Contentstack account and deliver it to your web or mobile properties.
354
+ It is an second parameter in which we want to place different manipulation key and
355
+ value in array form ImageTransform method is define for image manipulation with
356
+ different transform_params in second parameter in array form
357
+
358
+ Arguments:
359
+ image_url {str} -- On which we want to manipulate
360
+ **kwargs {object} -- parameter in which we want to place different manipulation key-worded, variable-length
361
+ argument list
362
+ It is a query form that we want to place different manipulation key and value
363
+
364
+ Returns:
365
+ str -- Image transformation url
366
+
367
+ ==============================
368
+
369
+ [Example]:
370
+
371
+ >>> from contentstack import Stack
372
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
373
+ >>> stack.image_transform('image_url', width=100, height=100)
374
+
375
+ ==============================
376
+
377
+ """
378
+
379
+ self.__image_transform_url = image_url
380
+ self.__image_params = kwargs
381
+ args = ['{0}={1}'.format(k, v) for k, v in kwargs.items()]
382
+ if args:
383
+ self.__image_transform_url += '?{0}'.format('&'.join(args))
384
+ return self.__image_transform_url
385
+
386
+ def collaborators(self):
387
+
388
+ """
389
+ A detailed information about collaborators with whome the stacks are shared.
390
+
391
+ Returns:
392
+ Stack -- Stack object so we can chain other functions.
393
+
394
+ ==============================
395
+
396
+ [Example]:
397
+
398
+ >>> from contentstack import Stack
399
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
400
+ >>> stack = stack.collaborators()
401
+ >>> result = stack.fetch()
402
+
403
+ ==============================
404
+
405
+ """
406
+
407
+ self.__query_params['include_collaborators'] = 'true'
408
+
409
+ return self
410
+
411
+ def include_stack_variables(self):
412
+
413
+ """
414
+ Stack variables are extra information about the stack, such as the description,
415
+ format of date, format of time, and so on. Users can include or exclude stack variables
416
+ in the response
417
+
418
+ Returns:
419
+ Stack -- Stack object so we can chain other functions.
420
+
421
+ ==============================
422
+
423
+ [Example]:
424
+
425
+ >>> from contentstack import Stack
426
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
427
+ >>> stack = stack.include_stack_variables()
428
+ >>> result = stack.fetch()
429
+
430
+ ==============================
431
+
432
+ """
433
+
434
+ self.__query_params['include_stack_variables'] = 'true'
435
+
436
+ return self
437
+
438
+ def include_discrete_variables(self):
439
+
440
+ """Discrete variables of the stack
441
+
442
+ Returns:
443
+ Stack -- Stack object so we can chain other functions.
444
+
445
+ ==============================
446
+
447
+ [Example]:
448
+
449
+ >>> from contentstack import Stack
450
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
451
+ >>> stack = stack.include_discrete_variables()
452
+ >>> result = stack.fetch()
453
+
454
+ ==============================
455
+ """
456
+
457
+ self.__query_params['include_discrete_variables'] = 'true'
458
+
459
+ return self
460
+
461
+ def include_count(self):
462
+
463
+ """
464
+ Includes count of stack response
465
+
466
+
467
+ Returns:
468
+ Stack -- Stack object so we can chain other functions.
469
+
470
+ ==============================
471
+
472
+ [Example]:
473
+
474
+ >>> from contentstack import Stack
475
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
476
+ >>> stack = stack.include_count()
477
+ >>> result = stack.fetch()
478
+
479
+ ==============================
480
+ """
481
+
482
+ self.__query_params['include_count'] = 'true'
483
+
484
+ return self
485
+
486
+ def fetch(self):
487
+
488
+ """
489
+ Fetches the stack reponse as per the functions applied
490
+
491
+ Returns:
492
+ dict -- result is the reponse of the stack
493
+
494
+ ==============================
495
+
496
+ [Example]:
497
+
498
+ >>> from contentstack import Stack
499
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
500
+ >>> stack = stack.include_count()
501
+ >>> result = stack.fetch()
502
+
503
+ ==============================
504
+
505
+ """
506
+ url = '{}/stacks'.format(self.config.endpoint)
507
+ result = self.__http_request.get_result(url, self.__query_params, self.__stack_headers)
508
+ return result
509
+
510
+ def sync(self, **kwargs):
511
+
512
+ """
513
+ content_type_uid -- You can also initialize sync with entries of
514
+ only specific content_type. To do this, use syncContentType and specify
515
+ the content type uid as its value. However, if you do this,
516
+ the subsequent syncs will only include the entries of the specified content_type.
517
+
518
+ from_date -- You can also initialize sync with entries published
519
+ after a specific date. To do this, use from_date
520
+ and specify the start date as its value.
521
+
522
+ locale -- You can also initialize sync with entries of only specific locales.
523
+ To do this, use syncLocale and specify the locale code as its value.
524
+ However, if you do this, the subsequent syncs will only include
525
+ the entries of the specified locales.
526
+
527
+ publish_type -- Use the type parameter to get a specific type of content.
528
+ You can pass one of the following values:
529
+
530
+ asset_published, entry_published, asset_unpublished,
531
+ asset_deleted, entry_unpublished, entry_deleted,
532
+ content_type_deleted.
533
+
534
+ If you do not specify any value, it will bring all published entries and published assets.
535
+
536
+ Raises:
537
+ KeyError: If headers of the stack is None or empty
538
+
539
+ Returns:
540
+ list[SyncResult] -- returns list of SyncResult
541
+
542
+ ==============================
543
+
544
+ [Example]:
545
+
546
+ >>> from contentstack import Stack
547
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
548
+ >>> result = stack.sync(content_type_uid='content_type_uid', from_date='date', locale='en-us', publish_type='asset_published')
549
+
550
+ ==============================
551
+
552
+ """
553
+
554
+ self.__sync_query["init"] = 'true'
555
+ if kwargs is not None and len(kwargs) > 0:
556
+ for key, value in kwargs.items():
557
+ self.__sync_query[key] = value
558
+ if self.__stack_headers is not None and len(self.__stack_headers) > 0:
559
+ if 'environment' in self.__stack_headers:
560
+ env = self.__stack_headers['environment']
561
+ self.__sync_query['environment'] = env
562
+ else:
563
+ raise KeyError("Kindly provide stack headers")
564
+
565
+ return self.__sync_request()
566
+
567
+ def pagination(self, pagination_token):
568
+
569
+ """
570
+ If the result of the initial sync (or subsequent sync) contains more than 100 records, the response would be
571
+ paginated. It provides pagination token in the response. However, you do not have to use the pagination token
572
+ manually to get the next batch, the SDK does that automatically until the sync is complete. Pagination token
573
+ can be used in case you want to fetch only selected batches. It is especially useful if the sync process is
574
+ interrupted midway (due to network issues, etc.). In such cases, this token can be used to restart the sync
575
+ process from where it was interrupted.
576
+
577
+ Arguments:
578
+ pagination_token {str} -- pagination_token
579
+
580
+ Raises:
581
+ KeyError: If pagination_token is None, empty or not str type
582
+
583
+ Returns:
584
+ list[SyncResult] -- list of SyncResult
585
+
586
+ ==============================
587
+
588
+ [Example]:
589
+
590
+ >>> from contentstack import Stack
591
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
592
+ >>> result = stack.pagination('blt8347235938759')
593
+
594
+ ==============================
595
+
596
+ """
597
+
598
+ if isinstance(pagination_token, str):
599
+ self.__sync_query = {'pagination_token': pagination_token}
600
+ else:
601
+ raise KeyError('Kindly provide valid pagination_token')
602
+ return self.__sync_request()
603
+
604
+ def sync_token(self, sync_token):
605
+
606
+ """
607
+ You can use the sync token (that you receive after initial sync)
608
+ to get the updated content next time.
609
+ The sync token fetches only the content that was added after your last sync,
610
+ and the details of the content that was deleted or updated.
611
+
612
+ Arguments:
613
+ sync_token {str} -- sync_token
614
+
615
+ Raises:
616
+ StackException: If sync_token is not str
617
+
618
+ Returns:
619
+ list[SyncResult] -- list of SyncResult object
620
+
621
+ ==============================
622
+
623
+ [Example]:
624
+
625
+ >>> from contentstack import Stack
626
+ >>> stack = Stack(api_key='stack_api_key', access_token='stack_access_token', environment='env')
627
+ >>> result = stack.sync_token('bltsomekeytoput')
628
+
629
+ ==============================
630
+
631
+ """
632
+
633
+ if isinstance(sync_token, str):
634
+ self.__sync_query = {'sync_token': sync_token}
635
+ else:
636
+ raise StackException('Kindly provide valid sync_token')
637
+ return self.__sync_request()
638
+
639
+ def __sync_request(self):
640
+
641
+ # This is useful to find sync_request for the stack
642
+ url = '{}/stacks/sync'.format(self.config.endpoint)
643
+ result = self.__http_request.get_result(url, self.__sync_query, self.__stack_headers)
644
+ return result
645
+
646
+
647
+ class SyncResult:
648
+
649
+ def __init__(self):
650
+ self.__resp: dict = {}
651
+ self.__items: list = []
652
+ self.__skip = int
653
+ self.__limit = int
654
+ self.__total_count = int
655
+ self.__sync_token = str
656
+ self.__pagination_token = str
657
+
658
+ def _configure(self, result: dict):
659
+ if result is not None and len(result) > 0:
660
+ self.__resp = result
661
+ if 'items' in self.__resp:
662
+ self.__items = self.__resp['items']
663
+ if 'skip' in self.__resp:
664
+ self.__skip = self.__resp['skip']
665
+ if 'limit' in self.__resp:
666
+ self.__limit = self.__resp['limit']
667
+ if 'total_count' in self.__resp:
668
+ self.__total_count = self.__resp['total_count']
669
+ if 'sync_token' in self.__resp:
670
+ self.__sync_token = self.__resp['sync_token']
671
+ if 'pagination_token' in self.__resp:
672
+ self.__pagination_token = self.__resp['pagination_token']
673
+ return self
674
+
675
+ @property
676
+ def json(self):
677
+
678
+ """
679
+ :return: Json response
680
+ """
681
+ return self.__resp
682
+
683
+ @property
684
+ def items(self):
685
+
686
+ """
687
+ :return: Total items
688
+ """
689
+ return self.__items
690
+
691
+ @property
692
+ def skip(self):
693
+
694
+ """
695
+ :return: skipped items
696
+ """
697
+ return self.__skip
698
+
699
+ @property
700
+ def limit(self):
701
+
702
+ """
703
+ :return: This property returns limit
704
+ :rtype: int
705
+ ==============================
706
+
707
+ Example:
708
+ >>> limit = SyncResult.limit
709
+
710
+ ==============================
711
+ """
712
+ return self.__limit
713
+
714
+ @property
715
+ def count(self):
716
+
717
+ """
718
+ :return: This property returns count
719
+ :rtype: int
720
+
721
+ ==============================
722
+
723
+ Example:
724
+ >>> count = SyncResult.count
725
+
726
+ ==============================
727
+ """
728
+ return self.__total_count
729
+
730
+ @property
731
+ def sync_token(self):
732
+
733
+ """
734
+ :return: This property returns sync_token
735
+ :rtype: str
736
+
737
+ ==============================
738
+
739
+ Example:
740
+ >>> sync_token = SyncResult.sync_token
741
+
742
+ ==============================
743
+ """
744
+ return self.__sync_token
745
+
746
+ @property
747
+ def pagination_token(self):
748
+
749
+ """
750
+ :return: This property returns pagination_token
751
+ :rtype: str
752
+
753
+ ==============================
754
+
755
+ Example:
756
+ >>> pagination_token = SyncResult.pagination_token
757
+
758
+ ==============================
759
+ """
760
+ return self.__pagination_token