Line data Source code
1 : import 'package:chatwoot_client_sdk/data/local/entity/chatwoot_contact.dart'; 2 : import 'package:chatwoot_client_sdk/data/local/entity/chatwoot_conversation.dart'; 3 : import 'package:chatwoot_client_sdk/data/local/local_storage.dart'; 4 : import 'package:chatwoot_client_sdk/data/remote/service/chatwoot_client_auth_service.dart'; 5 : import 'package:dio/dio.dart'; 6 : import 'package:synchronized/synchronized.dart' as synchronized; 7 : 8 : ///Intercepts network requests and attaches inbox identifier, contact identifiers, conversation identifiers 9 : class ChatwootClientApiInterceptor extends Interceptor { 10 : static const INTERCEPTOR_INBOX_IDENTIFIER_PLACEHOLDER = "{INBOX_IDENTIFIER}"; 11 : static const INTERCEPTOR_CONTACT_IDENTIFIER_PLACEHOLDER = 12 : "{CONTACT_IDENTIFIER}"; 13 : static const INTERCEPTOR_CONVERSATION_IDENTIFIER_PLACEHOLDER = 14 : "{CONVERSATION_IDENTIFIER}"; 15 : 16 : final String _inboxIdentifier; 17 : final LocalStorage _localStorage; 18 : final ChatwootClientAuthService _authService; 19 : final requestLock = synchronized.Lock(); 20 : final responseLock = synchronized.Lock(); 21 : 22 2 : ChatwootClientApiInterceptor( 23 : this._inboxIdentifier, this._localStorage, this._authService); 24 : 25 : /// Creates a new contact and conversation when no persisted contact is found when an api call is made 26 : @override 27 1 : Future<void> onRequest( 28 : RequestOptions options, RequestInterceptorHandler handler) async { 29 4 : await requestLock.synchronized(() async { 30 : RequestOptions newOptions = options; 31 3 : ChatwootContact? contact = _localStorage.contactDao.getContact(); 32 : ChatwootConversation? conversation = 33 3 : _localStorage.conversationDao.getConversation(); 34 : 35 : if (contact == null) { 36 : // create new contact from user if no token found 37 3 : contact = await _authService.createNewContact( 38 4 : _inboxIdentifier, _localStorage.userDao.getUser()); 39 3 : conversation = await _authService.createNewConversation( 40 2 : _inboxIdentifier, contact.contactIdentifier!); 41 4 : await _localStorage.conversationDao.saveConversation(conversation); 42 4 : await _localStorage.contactDao.saveContact(contact); 43 : } 44 : 45 : if (conversation == null) { 46 3 : conversation = await _authService.createNewConversation( 47 2 : _inboxIdentifier, contact.contactIdentifier!); 48 4 : await _localStorage.conversationDao.saveConversation(conversation); 49 : } 50 : 51 3 : newOptions.path = newOptions.path.replaceAll( 52 1 : INTERCEPTOR_INBOX_IDENTIFIER_PLACEHOLDER, _inboxIdentifier); 53 3 : newOptions.path = newOptions.path.replaceAll( 54 : INTERCEPTOR_CONTACT_IDENTIFIER_PLACEHOLDER, 55 1 : contact.contactIdentifier!); 56 3 : newOptions.path = newOptions.path.replaceAll( 57 : INTERCEPTOR_CONVERSATION_IDENTIFIER_PLACEHOLDER, 58 2 : "${conversation.id}"); 59 : 60 1 : handler.next(newOptions); 61 : }); 62 : } 63 : 64 : /// Clears and recreates contact when a 401 (Unauthorized), 403 (Forbidden) or 404 (Not found) 65 : /// response is returned from chatwoot public client api 66 : @override 67 1 : Future<void> onResponse( 68 : Response response, ResponseInterceptorHandler handler) async { 69 4 : await responseLock.synchronized(() async { 70 2 : if (response.statusCode == 401 || 71 2 : response.statusCode == 403 || 72 2 : response.statusCode == 404) { 73 3 : await _localStorage.clear(); 74 : 75 : // create new contact from user if unauthorized,forbidden or not found 76 3 : final contact = await _authService.createNewContact( 77 4 : _inboxIdentifier, _localStorage.userDao.getUser()); 78 3 : final conversation = await _authService.createNewConversation( 79 2 : _inboxIdentifier, contact.contactIdentifier!); 80 4 : await _localStorage.contactDao.saveContact(contact); 81 4 : await _localStorage.conversationDao.saveConversation(conversation); 82 : 83 1 : RequestOptions newOptions = response.requestOptions; 84 : 85 3 : newOptions.path = newOptions.path.replaceAll( 86 1 : INTERCEPTOR_INBOX_IDENTIFIER_PLACEHOLDER, _inboxIdentifier); 87 3 : newOptions.path = newOptions.path.replaceAll( 88 : INTERCEPTOR_CONTACT_IDENTIFIER_PLACEHOLDER, 89 1 : contact.contactIdentifier!); 90 3 : newOptions.path = newOptions.path.replaceAll( 91 : INTERCEPTOR_CONVERSATION_IDENTIFIER_PLACEHOLDER, 92 2 : "${conversation.id}"); 93 : 94 : //use authservice's dio without the interceptor for subsequent call 95 5 : handler.next(await _authService.dio.fetch(newOptions)); 96 : } else { 97 : // if response is not unauthorized, forbidden or not found forward response 98 1 : handler.next(response); 99 : } 100 : }); 101 : } 102 : } 103 : 104 : extension Range on num { 105 2 : bool isBetween(num from, num to) { 106 4 : return from < this && this < to; 107 : } 108 : }