mangoapps-ex-sdk-ruby 19.1.1 โ†’ 19.1.2

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.
data/README.md CHANGED
@@ -1,25 +1,7 @@
1
1
  # MangoApps Ruby SDK
2
2
 
3
- A clean, **real TDD** Ruby SDK for MangoApps APIs with OAuth2/OpenID Connect authentication. Features intuitive dot notation access, automatic response wrapping, and comprehensive real-world testing with actual MangoApps credentials.
4
-
5
- ## Features
6
-
7
- - ๐Ÿ” **OAuth2/OpenID Connect** authentication with automatic token refresh and userinfo endpoint
8
- - ๐Ÿ”‘ **Internal API Authentication** - Alternative authentication using API keys for server-to-server communication
9
- - ๐Ÿค– **Automatic Authentication Detection** - Automatically detects and prioritizes Internal API over OAuth credentials
10
- - ๐Ÿ“„ **Pagination Support** - All list APIs support optional page and limit parameters for efficient data retrieval
11
- - ๐Ÿš€ **Simple API** with intuitive method names and clean dot notation
12
- - ๐Ÿงช **Real TDD** - no mocking, only actual OAuth testing
13
- - ๐Ÿ”„ **Automatic retries** with exponential backoff
14
- - ๐Ÿ“ **Comprehensive error handling** with specific exception types
15
- - ๐Ÿ›ก๏ธ **Security-first** design with PKCE support and HTTPS enforcement
16
- - ๐Ÿ”ง **Environment variable configuration** for secure credentials
17
- - ๐Ÿ“š **Well-documented** with examples and guides
18
- - โœจ **Clean Response API** - Automatic response wrapping with intuitive dot notation access
19
- - ๐Ÿ”” **Notifications Module** - User priority items including requests, events, quizzes, surveys, tasks, and todos
20
- - ๐Ÿ“ฐ **Feeds Module** - User activity feeds with unread counts and feed details
21
- - ๐Ÿ“ **Posts Module** - Get all posts with filtering options and post management
22
- - ๐Ÿ”’ **HTTPS Always** - All OAuth endpoints use HTTPS with automatic redirect handling
3
+ A clean, Ruby SDK for MangoApps APIs with OAuth2/OpenID Connect authentication.
4
+
23
5
 
24
6
  ## Installation
25
7
 
@@ -41,83 +23,6 @@ Or install it directly:
41
23
  gem install mangoapps-ex-sdk-ruby
42
24
  ```
43
25
 
44
- **Current Version**: 0.15.2 (includes OAuth userinfo endpoint, HTTPS security enhancements, Internal API authentication, automatic authentication detection, and pagination support)
45
-
46
- ## Quick Start
47
-
48
- ### 1. Environment Setup
49
-
50
- For testing purposes, create a `.env` file with your MangoApps credentials:
51
-
52
- ```bash
53
- # .env (for testing only)
54
- MANGOAPPS_DOMAIN=yourdomain.mangoapps.com
55
-
56
- # OAuth2 Authentication (Option 1)
57
- MANGOAPPS_CLIENT_ID=your_client_id_here
58
- MANGOAPPS_CLIENT_SECRET=your_client_secret_here
59
- MANGOAPPS_REDIRECT_URI=https://localhost:3000/oauth/callback
60
- MANGOAPPS_SCOPE=openid profile email
61
-
62
- # Internal API Authentication (Option 2 - Alternative to OAuth)
63
- # Contact MangoApps support team to get your Internal API Key and Secret
64
- MANGOAPPS_INTERNAL_API_KEY=your_internal_api_key
65
- MANGOAPPS_INTERNAL_API_SECRET=your_internal_api_secret
66
- ```
67
-
68
- **Note**: The `.env` file is only used for testing. In production, you should handle token storage and management according to your application's security requirements.
69
-
70
- ### 2. Rails Integration
71
-
72
- For Rails applications, see our comprehensive [Rails OAuth Guide](RAILS_OAUTH_GUIDE.md) which includes:
73
-
74
- - Simple OAuth 2.0 flow implementation
75
- - Token storage and management
76
- - OAuth userinfo endpoint integration
77
- - HTTPS security enforcement
78
- - Complete callback handling
79
-
80
- ### 3. Configuration
81
-
82
- ```ruby
83
- require "mangoapps"
84
-
85
- # For testing - automatically loads from .env file and detects authentication method
86
- config = MangoApps::Config.new
87
- # Output: ๐Ÿ”‘ Using Internal API authentication (if internal API credentials found)
88
- # Output: ๐Ÿ” Using OAuth2 authentication (if OAuth credentials found)
89
-
90
- # For production - OAuth2 configuration
91
- config = MangoApps::Config.new(
92
- domain: "yourdomain.mangoapps.com",
93
- client_id: "your_client_id",
94
- client_secret: "your_client_secret",
95
- redirect_uri: "https://localhost:3000/oauth/callback",
96
- scope: "openid profile email"
97
- )
98
- client = MangoApps::Client.new(config)
99
-
100
- # For production - Internal API configuration (Alternative to OAuth)
101
- # Note: Contact MangoApps support team to get your Internal API Key and Secret
102
- config = MangoApps::Config.new(
103
- domain: "yourdomain.mangoapps.com",
104
- internal_api_key: "your_internal_api_key",
105
- internal_api_secret: "your_internal_api_secret"
106
- )
107
- client = MangoApps::Client.new(config)
108
- ```
109
-
110
- ### 3. OAuth Authentication
111
-
112
- #### Quick Start (Recommended)
113
- ```bash
114
- # Get OAuth token (interactive)
115
- ./run_auth.sh
116
-
117
- # Run tests to verify everything works
118
- ./run_tests.sh
119
- ```
120
-
121
26
  #### Basic Usage Example
122
27
  ```ruby
123
28
  require 'mangoapps-ex-sdk-ruby'
@@ -220,92 +125,7 @@ if wikis.wikis.any?
220
125
  end
221
126
  ```
222
127
 
223
- #### Manual OAuth Flow
224
- ```ruby
225
- # Generate authorization URL
226
- state = SecureRandom.hex(16)
227
- auth_url = client.authorization_url(state: state)
228
- puts "Open this URL to authorize: #{auth_url}"
229
-
230
- # After user authorizes, exchange code for tokens
231
- tokens = client.authenticate!(authorization_code: params[:code])
232
-
233
- # Store tokens securely in your application
234
- # (implementation depends on your storage solution)
235
- store_tokens(tokens.access_token, tokens.refresh_token)
236
-
237
- # Now you can make API calls with clean dot notation
238
- user = client.me
239
- puts "Welcome, #{user.user_profile.minimal_profile.name}!"
240
-
241
- # Get OAuth user info from userinfo endpoint
242
- userinfo = client.get_userinfo
243
- puts "OAuth User: #{userinfo.name} (#{userinfo.email})"
244
- puts "Subject ID: #{userinfo.sub}"
245
- puts "Username: #{userinfo.preferred_username}"
246
- ```
247
-
248
- #### OAuth Userinfo Endpoint
249
-
250
- The SDK provides access to the OAuth userinfo endpoint for getting authenticated user information:
251
-
252
- ```ruby
253
- # Get user info from OAuth userinfo endpoint
254
- userinfo = client.get_userinfo
255
-
256
- # Access OAuth user data with clean dot notation
257
- puts "Name: #{userinfo.name}"
258
- puts "Email: #{userinfo.email}"
259
- puts "Subject ID: #{userinfo.sub}"
260
- puts "Username: #{userinfo.preferred_username}"
261
-
262
- # Available OAuth userinfo fields:
263
- # - sub: Subject identifier (unique user ID)
264
- # - name: Full name
265
- # - email: Email address
266
- # - preferred_username: Username
267
- # - email_verified: Email verification status
268
- # - locale: User locale
269
- # - zoneinfo: Timezone information
270
- ```
271
-
272
- #### HTTPS Security Features
273
-
274
- The SDK enforces HTTPS for all OAuth operations:
275
-
276
- ```ruby
277
- # Automatic HTTPS enforcement
278
- # - Discovery endpoints always use HTTPS
279
- # - Userinfo endpoints always use HTTPS
280
- # - Automatic redirect handling (301/302)
281
- # - SSL certificate verification
282
-
283
- # Example: Even if discovery returns HTTP URLs, they're converted to HTTPS
284
- config = MangoApps::Config.new(domain: "example.mangoapps.com")
285
- client = MangoApps::Client.new(config)
286
-
287
- # This will automatically use HTTPS
288
- userinfo = client.get_userinfo
289
- ```
290
-
291
- ### 4. Automatic Authentication Detection
292
-
293
- The SDK automatically detects and prioritizes authentication methods:
294
-
295
- 1. **Internal API Credentials** (highest priority) - If `MANGOAPPS_INTERNAL_API_KEY` and `MANGOAPPS_INTERNAL_API_SECRET` are found
296
- 2. **OAuth Credentials** (fallback) - If `MANGOAPPS_CLIENT_ID` and `MANGOAPPS_CLIENT_SECRET` are found
297
-
298
- ```ruby
299
- # Automatic detection - no configuration needed
300
- config = MangoApps::Config.new
301
- # Shows: ๐Ÿ”‘ Using Internal API authentication (if internal API found)
302
- # Shows: ๐Ÿ” Using OAuth2 authentication (if OAuth found)
303
-
304
- client = MangoApps::Client.new(config)
305
- # All API calls work automatically with detected authentication method
306
- ```
307
-
308
- ### 5. Pagination Support
128
+ ### Pagination Support
309
129
 
310
130
  Most list APIs support optional pagination parameters for efficient data retrieval:
311
131
 
@@ -337,803 +157,6 @@ items = client.get_library_items(library_id, category_id, offset: 0, limit: 15)
337
157
  - Tasks: `page: 1, limit: 5` (smaller default for task management)
338
158
  - Posts: `offset: 0, limit: 20` with `filter_by: "all"`
339
159
 
340
- ### 6. Internal API Authentication (Alternative to OAuth)
341
-
342
- For applications that need direct API access without OAuth flow, you can use internal API authentication:
343
-
344
- > **๐Ÿ“ž Getting API Credentials**: Contact the MangoApps support team to obtain your Internal API Key and Secret for server-to-server authentication.
345
-
346
- ```ruby
347
- # Configure with internal API credentials
348
- config = MangoApps::Config.new(
349
- domain: "yourdomain.mangoapps.com",
350
- internal_api_key: "your_internal_api_key",
351
- internal_api_secret: "your_internal_api_secret",
352
- )
353
- client = MangoApps::Client.new(config)
354
-
355
- # Use the SDK normally - authentication is handled automatically
356
- user = client.me
357
- puts "Hello, #{user.user_profile.minimal_profile.name}!"
358
-
359
- # Get user info (uses /me endpoint for internal API)
360
- userinfo = client.get_userinfo
361
- puts "User: #{userinfo.name} (#{userinfo.email})"
362
-
363
- # All other API calls work the same way
364
- posts = client.get_all_posts
365
- notifications = client.notifications
366
- ```
367
-
368
- #### Internal API Headers
369
-
370
- The SDK automatically adds the required headers for internal API authentication:
371
-
372
- ```ruby
373
- # These headers are automatically added to all requests:
374
- # - Internal-Api-Key: your_api_key
375
- # - Internal-Api-Secret: your_api_secret
376
- ```
377
-
378
- #### When to Use Internal API vs OAuth
379
-
380
- - **OAuth2**: Use for user-facing applications where users need to authenticate
381
- - **Internal API**: Use for server-to-server communication, automation, or internal tools
382
-
383
- ## Response Format
384
-
385
- The SDK automatically wraps all API responses in a `MangoApps::Response` object that provides clean dot notation access:
386
-
387
- ```ruby
388
- # Clean dot notation access (automatic response wrapping):
389
- user = client.me
390
- name = user.user_profile.minimal_profile.name
391
- email = user.user_profile.minimal_profile.email
392
- ```
393
-
394
- ### Response Features
395
-
396
- - **๐ŸŽฏ Dot Notation Access**: `response.user.name` for clean, intuitive API access
397
- - **๐Ÿ”„ Automatic Wrapping**: All responses are automatically wrapped in `MangoApps::Response`
398
- - **๐Ÿ”— Hash Compatibility**: Still supports `[]` access if needed
399
- - **๐Ÿ“Š Enumerable Support**: Arrays and hashes work as expected
400
- - **๐Ÿ” Raw Data Access**: Use `response.raw_data` for original response
401
- - **โšก Type Safety**: Better IDE support and autocomplete
402
- - **๐ŸŽจ Clean Code**: No more verbose nested hash access
403
-
404
- ## API Resources
405
-
406
- ### Users Module
407
-
408
- ```ruby
409
- # Get current user profile
410
- user = client.me
411
-
412
- # Access user data with clean dot notation
413
- puts "User: #{user.user_profile.minimal_profile.name}"
414
- puts "Email: #{user.user_profile.minimal_profile.email}"
415
- puts "Points: #{user.user_profile.gamification.total_points}"
416
- puts "Followers: #{user.user_profile.user_data.followers}"
417
- ```
418
-
419
- ### Learn Module
420
-
421
- #### Course Catalog
422
- ```ruby
423
- # Get course catalog
424
- courses = client.course_catalog
425
-
426
- # Access course data with clean dot notation
427
- courses.courses.each do |course|
428
- puts "#{course.name} - #{course.course_type}"
429
- end
430
- ```
431
-
432
- #### Course Categories
433
- ```ruby
434
- # Get all course categories (no pagination)
435
- categories = client.course_categories
436
-
437
- # Access category data with clean dot notation
438
- categories.all_categories.each do |category|
439
- puts "#{category.name} - Position: #{category.position}"
440
- end
441
- ```
442
-
443
- #### Course Details
444
- ```ruby
445
- # Get detailed course information by course ID
446
- course_id = 604
447
- course = client.course_details(course_id)
448
-
449
- # Access course data with clean dot notation
450
- course_data = course.course
451
- puts "Course: #{course_data.name} (ID: #{course_data.id})"
452
- puts "Description: #{course_data.description}"
453
- puts "Type: #{course_data.course_type}"
454
- puts "Delivery Mode: #{course_data.delivery_mode}"
455
- puts "Instructors: #{course_data.instructors.length}"
456
- puts "Fields: #{course_data.fields.length}"
457
-
458
- # Access course URLs
459
- puts "Start Course URL: #{course_data.start_course_url}"
460
- puts "Go to Course URL: #{course_data.goto_course_url}"
461
-
462
- # Access course fields (details, certification, etc.)
463
- course_data.fields.each do |field|
464
- puts "Field: #{field.field_name}"
465
- field.course_sub_fields.each do |sub_field|
466
- puts " #{sub_field.field_name}: #{sub_field.field_value}"
467
- end
468
- end
469
- ```
470
-
471
- #### My Learning
472
- ```ruby
473
- # Get user's learning progress and courses
474
- learning = client.my_learning
475
-
476
- # Access user learning data with clean dot notation
477
- puts "User: #{learning.user_name} (ID: #{learning.user_id})"
478
- puts "Total training time: #{learning.total_training_time}"
479
- puts "Ongoing courses: #{learning.ongoing_course_count}"
480
- puts "Completed courses: #{learning.completed_course_count}"
481
- puts "Registered courses: #{learning.registered_course_count}"
482
-
483
- # Access learning sections
484
- learning.section.each do |section|
485
- puts "#{section.label} - #{section.count} courses"
486
-
487
- # Access courses in each section
488
- section.courses.each do |course|
489
- puts " ๐Ÿ“š #{course.name} - #{course.course_progress}% progress"
490
- end
491
- end
492
- ```
493
-
494
- ### Recognitions Module
495
-
496
- #### Award Categories
497
- ```ruby
498
- # Get award categories
499
- categories = client.award_categories
500
-
501
- # Access award category data with clean dot notation
502
- categories.award_categories.each do |category|
503
- puts "#{category.name} (ID: #{category.id}) - Permission: #{category.recipient_permission}"
504
- end
505
- ```
506
-
507
- #### Core Value Tags
508
- ```ruby
509
- # Get core value tags
510
- tags = client.core_value_tags
511
-
512
- # Access core value tag data with clean dot notation
513
- tags.core_value_tags.each do |tag|
514
- puts "#{tag.name} (ID: #{tag.id}) - Color: ##{tag.color}"
515
- end
516
- ```
517
-
518
- #### Leaderboard Info
519
- ```ruby
520
- # Get leaderboard information
521
- leaderboard = client.leaderboard_info
522
-
523
- # Check if leaderboard data is available
524
- if leaderboard.leaderboard_info
525
- # Access user leaderboard with clean dot notation
526
- leaderboard.leaderboard_info.user_info.each do |user|
527
- puts "๐Ÿ… #{user.name} (Rank: #{user.rank}) - Awards: #{user.award_count}"
528
- end
529
-
530
- # Access team leaderboard with clean dot notation
531
- leaderboard.leaderboard_info.team_info.each do |team|
532
- puts "๐Ÿ† #{team.name} (Rank: #{team.rank}) - Awards: #{team.award_count}"
533
- end
534
- else
535
- puts "No leaderboard data configured"
536
- end
537
- ```
538
-
539
-
540
-
541
- #### Get Awards List
542
- ```ruby
543
- # Get awards list for a specific category
544
- response = client.get_awards_list(category_id: 4303)
545
-
546
- # Access award data with clean dot notation
547
- response.get_awards_list.each do |award|
548
- puts "#{award.name} (ID: #{award.id})"
549
- puts " Description: #{award.description}"
550
- puts " Points: #{award.points}"
551
- puts " Reward Points: #{award.reward_points}"
552
- puts " Image: #{award.attachment_url}"
553
- end
554
- ```
555
-
556
- #### Get Profile Awards
557
- ```ruby
558
- # Get user profile awards
559
- response = client.get_profile_awards
560
-
561
- # Access core value tags with counts
562
- response.core_value_tags.each do |tag|
563
- puts "#{tag.name} (ID: #{tag.id}) - Count: #{tag.count}"
564
- end
565
-
566
- # Access award feeds
567
- response.feeds.each do |feed|
568
- puts "#{feed.feed_property.title} - Points: #{feed.recognition_points}"
569
- puts "From: #{feed.from_user.name}"
570
- puts "Body: #{feed.body}"
571
- end
572
- ```
573
-
574
-
575
- #### My Priority Items
576
- ```ruby
577
- # Get user's priority items
578
- response = client.my_priority_items
579
-
580
- # Access priority items data
581
- response.data.each do |item|
582
- puts "#{item.title} (ID: #{item.id}) - Count: #{item.count}"
583
- puts " Action Type: #{item.action_type}"
584
- puts " Icon: #{item.icon} (#{item.icon_color})"
585
- puts " Details: #{item.info_details}"
586
- end
587
-
588
- # Check response status
589
- puts "Success: #{response.success}"
590
- puts "Display Type: #{response.display_type}"
591
- ```
592
-
593
- #### Feeds
594
- ```ruby
595
- # Get user's activity feeds
596
- response = client.feeds
597
-
598
- # Access feeds data
599
- response.feeds.each do |feed|
600
- puts "#{feed.feed_property.title} (ID: #{feed.id})"
601
- puts " From: #{feed.from_user.name} | Group: #{feed.group_name}"
602
- puts " Type: #{feed.feed_type} | Category: #{feed.category}"
603
- puts " Created: #{Time.at(feed.created_at.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
604
- puts " Unread: #{feed.unread}"
605
- puts " Body: #{feed.body[0..100]}..."
606
- end
607
-
608
- # Access unread counts
609
- puts "Unread feeds: #{response.unread_counts.unread_feeds_count}"
610
- puts "Direct messages: #{response.unread_counts.direct_messages_count}"
611
- puts "What's new: #{response.unread_counts.whats_new_count}"
612
-
613
- # Check response metadata
614
- puts "Limit: #{response.limit} | Version: #{response.mangoapps_version}"
615
- ```
616
-
617
- #### Get All Posts
618
- ```ruby
619
- # Get all posts with filtering
620
- response = client.get_all_posts(filter_by: "all")
621
-
622
- # Access posts data
623
- response.feeds.each do |post|
624
- puts "#{post.tile.tile_name} (ID: #{post.id})"
625
- puts " From: #{post.from_user.name} | Group: #{post.group_name}"
626
- puts " Post ID: #{post.post_id} | View count: #{post.total_view_count}"
627
- puts " Created: #{Time.at(post.created_at.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
628
- puts " Comments: #{post.comments.length} | Likes: #{post.like_count}"
629
- puts " Content: #{post.tile.tile_content[0..100]}..."
630
- end
631
-
632
- # Access post configuration
633
- puts "Post view count visibility: #{response.post_view_count_visibility}"
634
- puts "Post view count link config: #{response.post_view_count_link_config}"
635
- ```
636
-
637
- #### Get Post By ID
638
- ```ruby
639
- # Get detailed post information by ID
640
- post_id = 59101
641
- response = client.get_post_by_id(post_id, full_description: "Y")
642
-
643
- # Access post details
644
- post = response.post
645
- puts "#{post.title} (ID: #{post.id})"
646
- puts " Created by: #{post.created_name} (ID: #{post.creator_by})"
647
- puts " Created: #{Time.at(post.created_at.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
648
- puts " Conversation: #{post.conversation_name}"
649
- puts " View count: #{post.total_view_count} | Likes: #{post.like_count} | Comments: #{post.comment_count}"
650
-
651
- # Access tile information
652
- if post.tile
653
- puts " Tile: #{post.tile.tile_name}"
654
- puts " Full description: #{post.tile.tile_full_description[0..200]}..."
655
- puts " Image: #{post.tile.tile_image}"
656
- end
657
-
658
- # Check post permissions
659
- puts " Can edit: #{post.can_edit} | Can comment: #{post.can_comment} | Can delete: #{post.can_delete}"
660
- puts " Is draft: #{post.is_draft} | Archived: #{post.archived}"
661
- ```
662
-
663
- #### Libraries Management
664
- ```ruby
665
- # Get user's document libraries
666
- libraries = client.get_libraries
667
-
668
- # Access libraries data
669
- puts "๐Ÿ“š User Libraries:"
670
- libraries.libraries.each do |library|
671
- puts " โ€ข #{library.name} (ID: #{library.id})"
672
- puts " Type: #{library.library_type} | View: #{library.view_mode}"
673
- puts " Items: #{library.total_items_count} | Categories: #{library.categories.length}"
674
- puts " Edit access: #{library.edit_access} | Position: #{library.position}"
675
- puts " Banner: #{library.banner_color} | Icon: #{library.icon_properties.color}"
676
-
677
- # Access library categories
678
- if library.categories.any?
679
- puts " Categories:"
680
- library.categories.first(3).each do |category|
681
- puts " - #{category.name} (#{category.library_items_count} items)"
682
- end
683
- end
684
- puts ""
685
- end
686
- ```
687
-
688
- #### Get Library Categories
689
- ```ruby
690
- # Get detailed library information and categories by library ID
691
- library_id = 9776
692
- response = client.get_library_categories(library_id)
693
-
694
- # Access library details
695
- library = response.library
696
- puts "#{library.name} (ID: #{library.id})"
697
- puts " Type: #{library.library_type} | View: #{library.view_mode}"
698
- puts " Description: #{library.description}"
699
- puts " Total items: #{library.total_items_count} | Categories: #{library.categories.length}"
700
- puts " Edit access: #{library.edit_access} | Position: #{library.position}"
701
-
702
- # Access library properties
703
- puts " Banner color: #{library.banner_color} | Icon color enabled: #{library.enable_icon_color}"
704
- puts " Created: #{Time.at(library.created_at.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
705
- puts " Updated: #{Time.at(library.updated_at.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
706
-
707
- # Access icon properties
708
- if library.icon_properties
709
- puts " Icon color: #{library.icon_properties.color} | Icon class: #{library.icon_properties.class}"
710
- end
711
-
712
- # Access categories
713
- if library.categories.any?
714
- puts " Categories:"
715
- library.categories.each do |category|
716
- puts " โ€ข #{category.name} (ID: #{category.id})"
717
- puts " Items: #{category.library_items_count} | Rank: #{category.rank}"
718
- puts " Is system: #{category.is_system} | Icon: #{category.icon || 'None'}"
719
- if category.description
720
- puts " Description: #{category.description[0..100]}..."
721
- end
722
- end
723
- end
724
- ```
725
-
726
- #### Get Library Items
727
- ```ruby
728
- # Get library items by library ID and category ID
729
- library_id = 9776
730
- category_id = 50114
731
- response = client.get_library_items(library_id, category_id)
732
-
733
- # Access library items data
734
- puts "Category: #{response.category_name}"
735
- puts "View mode: #{response.view_mode} | Library type: #{response.library_type}"
736
- puts "Library ID: #{response.library_id} | Can add: #{response.can_add}"
737
- puts "Icon color: #{response.enable_icon_color}"
738
-
739
- # Access library items
740
- if response.library_items.any?
741
- puts "Library Items:"
742
- response.library_items.each do |item|
743
- puts " โ€ข #{item.name} (ID: #{item.id})"
744
- puts " Link type: #{item.link_type} | Link: #{item.link[0..50]}..."
745
-
746
- # Handle different link types
747
- if item.link_type == "ExternalLink"
748
- if item.icon_properties
749
- puts " Icon: #{item.icon_properties.color} | Class: #{item.icon_properties.class}"
750
- end
751
- elsif item.link_type == "Attachment"
752
- puts " Attachment ID: #{item.attachment_id} | File type: #{item.file_type}"
753
- puts " Likes: #{item.likes_count} | Is liked: #{item.is_liked}"
754
- puts " Image: #{item.image_url}"
755
- puts " Short URL: #{item.short_url[0..50]}..."
756
- end
757
- end
758
- else
759
- puts "No library items found"
760
- end
761
- ```
762
-
763
- #### Trackers Management
764
- ```ruby
765
- # Get user's trackers
766
- trackers = client.get_trackers
767
-
768
- # Access trackers data
769
- puts "๐Ÿ“Š User Trackers:"
770
- puts " Total trackers: #{trackers.trackers.length}"
771
- puts " Transaction ID: #{trackers.transaction_id || 'None'}"
772
- puts ""
773
-
774
- # Display recent trackers
775
- puts "๐Ÿ“Š Recent Trackers:"
776
- trackers.trackers.first(5).each do |tracker|
777
- puts " โ€ข #{tracker.name} (ID: #{tracker.id})"
778
- puts " Last submission: #{Time.at(tracker.last_submission_date.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
779
- puts " Conversation: #{tracker.conversation_name} (ID: #{tracker.conversation_id})"
780
- puts " Pinned: #{tracker.is_pinned} | Can share: #{tracker.can_share}"
781
-
782
- # Access tracker icon info
783
- if tracker.tracker_icon_info
784
- puts " Icon: #{tracker.tracker_icon_info.color_code} | Class: #{tracker.tracker_icon_info.icon_url}"
785
- end
786
-
787
- puts " MLink: #{tracker.mlink[0..50]}..."
788
- puts ""
789
- end
790
- ```
791
-
792
- #### Attachments Management
793
- ```ruby
794
- # Get user's folders
795
- folders = client.get_folders
796
-
797
- # Access folders data
798
- puts "๐Ÿ“ User Folders:"
799
- puts " Total folders: #{folders.folders.length}"
800
- puts " Transaction ID: #{folders.transaction_id || 'None'}"
801
- puts ""
802
-
803
- # Display folders
804
- puts "๐Ÿ“ Available Folders:"
805
- folders.folders.each do |folder|
806
- puts " โ€ข #{folder.name} (ID: #{folder.id})"
807
- puts " Path: #{folder.relativePath}"
808
- puts " Child count: #{folder.child_count} | Can save: #{folder.can_save}"
809
- puts " Pinned: #{folder.is_pinned} | Virtual: #{folder.is_virtual_folder}"
810
- puts " Folder rel: #{folder.folder_rel} | Type: #{folder.folder_type_from_db || 'None'}"
811
- puts " Show in upload: #{folder.show_in_upload} | Show in move: #{folder.show_in_move}"
812
- puts " Filter: #{folder.filter} | Show permissions: #{folder.show_permission_options}"
813
-
814
- # Show conversation and user IDs if available
815
- if folder.conversation_id
816
- puts " Conversation ID: #{folder.conversation_id}"
817
- end
818
- if folder.user_id
819
- puts " User ID: #{folder.user_id}"
820
- end
821
-
822
- puts ""
823
- end
824
- ```
825
-
826
- #### File and Folder Management
827
- ```ruby
828
- # Get user's folders first
829
- folders = client.get_folders
830
-
831
- # Find a folder with content
832
- if folders.folders.any?
833
- folder = folders.folders.find { |f| f.child_count.to_i > 0 }
834
-
835
- if folder
836
- puts "๐Ÿ“ Exploring folder: #{folder.name} (ID: #{folder.id})"
837
-
838
- # Get files and folders inside this folder
839
- folder_contents = client.get_folder_files(folder.id, include_folders: "Y")
840
-
841
- puts "๐Ÿ“ Folder Contents:"
842
- puts " Folder name: #{folder_contents.name}"
843
- puts " Total count: #{folder_contents.total_count}"
844
- puts " Role: #{folder_contents.role_name}"
845
- puts " Domain suspended: #{folder_contents.is_domain_suspended}"
846
- puts " Show in upload: #{folder_contents.show_in_upload}"
847
- puts ""
848
-
849
- # Display files and folders
850
- puts "๐Ÿ“ Files and Folders:"
851
- folder_contents.files.first(10).each do |item|
852
- puts " โ€ข #{item.filename} (ID: #{item.id})"
853
- puts " Type: #{item.is_folder ? 'Folder' : 'File'}"
854
- puts " Size: #{item.size} bytes"
855
- puts " Uploader: #{item.uploader_name} (ID: #{item.user_id})"
856
- puts " Updated: #{Time.at(item.updated_at.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
857
- puts " Visibility: #{item.visibility} | Privacy: #{item.privacy_type}"
858
- puts " Pinned: #{item.is_pinned} | Liked: #{item.is_liked}"
859
- puts " Can save: #{item.can_save} | Show permissions: #{item.show_permission_options}"
860
-
861
- # Show role permissions
862
- if item.role
863
- puts " Permissions: Edit: #{item.role.can_edit} | Share: #{item.role.can_share} | Restore: #{item.role.can_restore}"
864
- end
865
-
866
- # Show links
867
- if item.mLink
868
- puts " MLink: #{item.mLink[0..50]}..."
869
- end
870
-
871
- puts ""
872
- end
873
- else
874
- puts "No folders with content found"
875
- end
876
- else
877
- puts "No folders found"
878
- end
879
- ```
880
-
881
- #### Task Management
882
- ```ruby
883
- # Get user's tasks with filtering and pagination
884
- tasks = client.get_tasks(filter: "Pending_Tasks", page: 1, limit: 5)
885
-
886
- # Access tasks data
887
- puts "๐Ÿ“‹ User Tasks:"
888
- puts " Total tasks: #{tasks.tasks.task.length}"
889
- puts " Transaction ID: #{tasks.transaction_id || 'None'}"
890
- puts ""
891
-
892
- # Display tasks
893
- puts "๐Ÿ“‹ Task List:"
894
- tasks.tasks.task.each do |task|
895
- puts " โ€ข #{task.task_title} (ID: #{task.id})"
896
- puts " Status: #{task.status} | Bucket: #{task.bucket}"
897
- puts " Assigned to: #{task.assigned_to_name} (ID: #{task.assigned_to})"
898
- puts " Created by: #{task.creator_name} (ID: #{task.creator_id})"
899
- puts " Created: #{Time.at(task.created_at.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
900
- puts " Assigned: #{Time.at(task.assigned_on.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
901
- puts " Due: #{task.due} | Due on: #{task.due_on ? Time.at(task.due_on.to_i).strftime('%Y-%m-%d %H:%M:%S') : 'None'}"
902
- puts " Is overdue: #{task.is_overdue} | Can be started: #{task.task_can_be_started}"
903
- puts " Milestone: #{task.milestone_name || 'None'} (ID: #{task.milestone_id || 'None'})"
904
- puts " Project: #{task.conversation_name} (ID: #{task.project_id})"
905
- puts " Visibility: #{task.visibility} | Priority: #{task.personal_priority}"
906
-
907
- # Show reviewers
908
- if task.reviewers && task.reviewers.reviewer
909
- reviewers = task.reviewers.reviewer
910
- puts " Reviewers: #{reviewers.length} reviewers"
911
- reviewers.first(3).each do |reviewer|
912
- puts " - #{reviewer.user_name} (Status: #{reviewer.status})"
913
- end
914
- end
915
-
916
- # Show next actions
917
- if task.next_actions && task.next_actions.action
918
- actions = task.next_actions.action
919
- puts " Available actions: #{actions.join(', ')}"
920
- end
921
-
922
- # Show links
923
- if task.mlink
924
- puts " MLink: #{task.mlink[0..50]}..."
925
- end
926
-
927
- puts ""
928
- end
929
- ```
930
-
931
- #### Task Details Management
932
- ```ruby
933
- # Get detailed information for a specific task
934
- task_details = client.get_task_details("394153")
935
-
936
- # Access task details data
937
- puts "๐Ÿ“‹ Task Details:"
938
- puts " Task ID: #{task_details.task.id}"
939
- puts " Title: #{task_details.task.task_title}"
940
- puts " Status: #{task_details.task.status} | Bucket: #{task_details.task.bucket}"
941
- puts " Assigned to: #{task_details.task.assigned_to_name} (ID: #{task_details.task.assigned_to})"
942
- puts " Created by: #{task_details.task.creator_name} (ID: #{task_details.task.creator_id})"
943
- puts " Created: #{Time.at(task_details.task.created_at.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
944
- puts " Assigned: #{Time.at(task_details.task.assigned_on.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
945
- puts " Due: #{task_details.task.due} | Due on: #{task_details.task.due_on ? Time.at(task_details.task.due_on.to_i).strftime('%Y-%m-%d %H:%M:%S') : 'None'}"
946
- puts " Is overdue: #{task_details.task.is_overdue} | Can be started: #{task_details.task.task_can_be_started}"
947
- puts " Milestone: #{task_details.task.milestone_name || 'None'} (ID: #{task_details.task.milestone_id || 'None'})"
948
- puts " Project: #{task_details.task.conversation_name} (ID: #{task_details.task.project_id})"
949
- puts " Visibility: #{task_details.task.visibility} | Priority: #{task_details.task.personal_priority}"
950
- puts " Transaction ID: #{task_details.transaction_id || 'None'}"
951
- puts ""
952
-
953
- # Show task timeline
954
- if task_details.task.started_on
955
- puts "๐Ÿ“… Started: #{Time.at(task_details.task.started_on.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
956
- end
957
- if task_details.task.finished_on
958
- puts "๐Ÿ“… Finished: #{Time.at(task_details.task.finished_on.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
959
- end
960
- if task_details.task.delivered_on
961
- puts "๐Ÿ“… Delivered: #{Time.at(task_details.task.delivered_on.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
962
- end
963
-
964
- # Show task history
965
- if task_details.task.reopened_on
966
- puts "๐Ÿ“… Reopened: #{Time.at(task_details.task.reopened_on.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
967
- end
968
- if task_details.task.restarted_on
969
- puts "๐Ÿ“… Restarted: #{Time.at(task_details.task.restarted_on.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
970
- end
971
-
972
- # Show reviewers
973
- if task_details.task.reviewers && task_details.task.reviewers.reviewer
974
- reviewers = task_details.task.reviewers.reviewer
975
- puts "๐Ÿ‘ฅ Reviewers: #{reviewers.length} reviewers"
976
- reviewers.first(5).each do |reviewer|
977
- puts " - #{reviewer.user_name} (Status: #{reviewer.status})"
978
- end
979
- end
980
-
981
- # Show next actions
982
- if task_details.task.next_actions && task_details.task.next_actions.action
983
- actions = task_details.task.next_actions.action
984
- puts "โšก Available actions: #{actions.join(', ')}"
985
- end
986
-
987
- # Show task content
988
- if task_details.task.name
989
- puts "๐Ÿ“ Task content: #{task_details.task.name[0..200]}..."
990
- end
991
- if task_details.task.notes
992
- puts "๐Ÿ“ Notes: #{task_details.task.notes[0..200]}..."
993
- end
994
-
995
- # Show links
996
- if task_details.task.mlink
997
- puts "๐Ÿ”— MLink: #{task_details.task.mlink}"
998
- end
999
-
1000
- # Show attachments
1001
- if task_details.task.attachments
1002
- puts "๐Ÿ“Ž Attachments: #{task_details.task.attachments.length} attachments"
1003
- end
1004
- if task_details.task.attachment_references
1005
- puts "๐Ÿ“Ž Attachment references: #{task_details.task.attachment_references.length} references"
1006
- end
1007
- ```
1008
-
1009
- #### Wiki Management
1010
- ```ruby
1011
- # Get user's wikis with filtering and pagination
1012
- wikis = client.get_wikis(mode: "my", limit: 20, offset: 0)
1013
-
1014
- # Access wikis data
1015
- puts "๐Ÿ“š User Wikis:"
1016
- puts " Total wikis: #{wikis.wikis.length}"
1017
- puts " Transaction ID: #{wikis.transaction_id || 'None'}"
1018
- puts ""
1019
-
1020
- # Display wikis
1021
- puts "๐Ÿ“š Wiki List:"
1022
- wikis.wikis.each do |wiki|
1023
- puts " โ€ข #{wiki.title} (ID: #{wiki.id})"
1024
- puts " Updated: #{Time.at(wiki.updated_at.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
1025
- puts " Children: #{wiki.children_count} | Can edit: #{wiki.can_edit}"
1026
- puts " Conversation: #{wiki.conversation_name || 'None'} (ID: #{wiki.conversation_id})"
1027
- puts " Is draft: #{wiki.is_draft} | PDF access: #{wiki.generate_pdf_access}"
1028
- puts " User: #{wiki.user_name || 'None'} | Status: #{wiki.status || 'None'}"
1029
- puts " Governance enabled: #{wiki.governance_enabled} | Governance date: #{wiki.governance_date || 'None'}"
1030
-
1031
- # Show icon properties
1032
- if wiki.icon_properties
1033
- background_color = wiki.icon_properties.respond_to?(:'background-color') ? wiki.icon_properties.send(:'background-color') : nil
1034
- puts " Icon: #{wiki.icon_properties.class} | Color: #{background_color || 'None'}"
1035
- end
1036
-
1037
- # Show user image URL
1038
- if wiki.user_image_url
1039
- puts " User image: #{wiki.user_image_url[0..50]}..."
1040
- end
1041
-
1042
- puts ""
1043
- end
1044
- ```
1045
-
1046
- #### Wiki Details Management
1047
- ```ruby
1048
- # Get detailed information for a specific wiki
1049
- wiki_details = client.get_wiki_details("7212")
1050
-
1051
- # Access wiki details data
1052
- puts "๐Ÿ“š Wiki Details:"
1053
- puts " Wiki ID: #{wiki_details.wiki.details.id}"
1054
- puts " Title: #{wiki_details.wiki.details.title}"
1055
- puts " Status: #{wiki_details.wiki.details.status} | Platform: #{wiki_details.wiki.details.platform}"
1056
- puts " Created: #{Time.at(wiki_details.wiki.details.created_at.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
1057
- puts " Updated: #{Time.at(wiki_details.wiki.details.updated_at.to_i).strftime('%Y-%m-%d %H:%M:%S')}"
1058
- puts " Modified: #{wiki_details.wiki.details.modified_on}"
1059
- puts " Created by: #{wiki_details.wiki.details.created_by_name} (ID: #{wiki_details.wiki.details.user_id})"
1060
- puts " Updated by: #{wiki_details.wiki.details.updated_by_name} (ID: #{wiki_details.wiki.details.last_updated_by})"
1061
- puts " Conversation: #{wiki_details.wiki.details.conversation_name} (ID: #{wiki_details.wiki.details.conversation_id})"
1062
- puts " Read count: #{wiki_details.wiki.details.total_read_count} | Children: #{wiki_details.wiki.details.children_count}"
1063
- puts " Edit permissions: #{wiki_details.wiki.details.edit_permissions} | Commentable: #{wiki_details.wiki.details.is_commentable}"
1064
- puts " Generate PDF access: #{wiki_details.wiki.details.generate_pdf_access} | Show TOC: #{wiki_details.wiki.details.show_toc}"
1065
- puts " Has TOC: #{wiki_details.wiki.details.has_toc} | Archived: #{wiki_details.wiki.details.archived}"
1066
- puts " Domain ID: #{wiki_details.wiki.details.domain_id} | Feed ID: #{wiki_details.wiki.details.feed_id}"
1067
- puts ""
1068
-
1069
- # Show wiki content
1070
- if wiki_details.wiki.details.description
1071
- puts "๐Ÿ“ Description: #{wiki_details.wiki.details.description[0..300]}..."
1072
- end
1073
-
1074
- # Show banner URL
1075
- if wiki_details.wiki.details.banner_url
1076
- puts "๐Ÿ–ผ๏ธ Banner URL: #{wiki_details.wiki.details.banner_url}"
1077
- end
1078
-
1079
- # Show wiki permissions
1080
- puts "๐Ÿ” Wiki Permissions:"
1081
- puts " Can comment: #{wiki_details.wiki.can_comment}"
1082
- puts " Can edit: #{wiki_details.wiki.can_edit}"
1083
- puts " Can delete: #{wiki_details.wiki.can_delete}"
1084
- puts " Can rename: #{wiki_details.wiki.can_rename}"
1085
- puts " Can move: #{wiki_details.wiki.can_move}"
1086
- puts " Can duplicate: #{wiki_details.wiki.can_duplicate}"
1087
-
1088
- # Show wiki links
1089
- if wiki_details.wiki.mlink
1090
- puts "๐Ÿ”— MLink: #{wiki_details.wiki.mlink}"
1091
- end
1092
-
1093
- # Show attachments
1094
- if wiki_details.wiki.attachments
1095
- puts "๐Ÿ“Ž Attachments: #{wiki_details.wiki.attachments.length} attachments"
1096
- end
1097
- if wiki_details.wiki.attachment_references
1098
- puts "๐Ÿ“Ž Attachment references: #{wiki_details.wiki.attachment_references.length} references"
1099
- end
1100
-
1101
- # Show reactions
1102
- if wiki_details.wiki.reactions
1103
- reactions = wiki_details.wiki.reactions
1104
- puts "๐Ÿ‘ Reactions: Like: #{reactions.like_count}, Superlike: #{reactions.superlike_count}"
1105
- puts "๐Ÿ˜„ Reactions: Haha: #{reactions.haha_count}, Yay: #{reactions.yay_count}, Wow: #{reactions.wow_count}, Sad: #{reactions.sad_count}"
1106
- puts "๐Ÿ‘ค User reactions: Liked: #{reactions.liked}, Superliked: #{reactions.superliked}"
1107
- end
1108
-
1109
- # Show reaction data
1110
- if wiki_details.wiki.reaction_data
1111
- reaction_data = wiki_details.wiki.reaction_data
1112
- puts "๐Ÿ“Š Reaction data: #{reaction_data.length} reaction types"
1113
- reaction_data.each do |reaction|
1114
- puts " - #{reaction.label}: #{reaction.count} (Reacted: #{reaction.reacted})"
1115
- end
1116
- end
1117
-
1118
- # Show comment count
1119
- puts "๐Ÿ’ฌ Comment count: #{wiki_details.wiki.comment_count}"
1120
-
1121
- # Show wiki status
1122
- puts "๐Ÿ“Œ Is pinned: #{wiki_details.wiki.is_pinned}"
1123
- puts "๐Ÿ“„ Is draft: #{wiki_details.wiki.is_draft}"
1124
- puts "๐Ÿ”’ Governance enabled: #{wiki_details.wiki.governance_enabled}"
1125
-
1126
- # Show hashtags
1127
- if wiki_details.wiki.hashtags
1128
- hashtags = wiki_details.wiki.hashtags
1129
- puts "๐Ÿท๏ธ Hashtags: #{hashtags.length} hashtags"
1130
- hashtags.each do |hashtag|
1131
- puts " - #{hashtag}"
1132
- end
1133
- end
1134
- ```
1135
-
1136
-
1137
160
  ## Available Modules
1138
161
 
1139
162
  ### โœ… Currently Implemented
@@ -1737,64 +760,6 @@ config = MangoApps::Config.new(
1737
760
  # - SSL certificate verification enabled
1738
761
  ```
1739
762
 
1740
-
1741
- ## Development
1742
-
1743
- ### For SDK Users
1744
-
1745
- If you're using this SDK in your application, you only need:
1746
-
1747
- 1. **Install the gem**: `gem install mangoapps-ex-sdk-ruby`
1748
- 2. **Configure OAuth**: Set up your MangoApps OAuth credentials
1749
- 3. **Start coding**: Use the examples above
1750
-
1751
- ### For SDK Developers
1752
-
1753
- If you're contributing to or extending this SDK, see our comprehensive developer documentation:
1754
-
1755
- ๐Ÿ“š **[DEVELOPER.md](DEVELOPER.md)** - Complete guide for SDK development including:
1756
- - Adding new APIs and modules
1757
- - Testing guidelines (real TDD approach)
1758
- - Development workflow
1759
- - Code style and standards
1760
- - Module architecture
1761
- - Error handling
1762
- - Documentation standards
1763
- - Release process
1764
-
1765
- ### Quick Development Setup
1766
-
1767
- ```bash
1768
- git clone https://github.com/MangoAppsInc/mangoapps-ex-sdk-ruby.git
1769
- cd mangoapps-ex-sdk-ruby
1770
- cp .env.example .env
1771
- # Edit .env with your MangoApps credentials (for testing only)
1772
- bundle install
1773
- ```
1774
-
1775
- ### Testing the SDK
1776
-
1777
- This SDK uses **real TDD** - no mocking, only actual OAuth testing:
1778
-
1779
- ```bash
1780
- # Get OAuth token (first time or when expired)
1781
- ./run_auth.sh
1782
-
1783
- # Run tests
1784
- ./run_tests.sh
1785
-
1786
- # Run specific module tests
1787
- ./run_tests.sh learn
1788
- ./run_tests.sh users
1789
- ./run_tests.sh recognitions
1790
- ./run_tests.sh notifications
1791
- ./run_tests.sh feeds
1792
- ./run_tests.sh posts
1793
-
1794
- # Interactive testing
1795
- ./run_irb.sh
1796
- ```
1797
-
1798
763
  ### Current API Coverage
1799
764
 
1800
765
  - โœ… **Learn Module**: Course catalog, categories, course details, and my learning (4 endpoints)
@@ -1838,4 +803,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
1838
803
 
1839
804
  ## Changelog
1840
805
 
1841
- See [CHANGELOG.md](CHANGELOG.md) for a list of changes and version history.
806
+ See [CHANGELOG.md](CHANGELOG.md) for a list of changes and version history.