1
1
use std:: collections:: HashMap ;
2
2
use reqwest;
3
- use log:: { debug, warn, error} ;
3
+ use log:: { debug, warn, error, info } ;
4
4
use reqwest:: header:: { AUTHORIZATION , CONTENT_TYPE , HeaderMap } ;
5
5
use tokio;
6
6
use serde_json;
@@ -9,17 +9,19 @@ use futures::channel::mpsc::{Receiver, Sender};
9
9
use crate :: config:: Config ;
10
10
use crate :: data_structures:: { JsonList , StatusMessage , GetBlobConfig , GetContentConfig , AuthResult ,
11
11
ContentToRetrieve , CliArgs } ;
12
+ use anyhow:: Result ;
13
+ use serde_json:: Value ;
12
14
13
15
14
16
/// Return a logged in API connection object. Use the Headers value to make API requests.
15
- pub fn get_api_connection ( args : CliArgs , config : Config ) -> ApiConnection {
17
+ pub async fn get_api_connection ( args : CliArgs , config : Config ) -> ApiConnection {
16
18
17
19
let mut api = ApiConnection {
18
20
args,
19
21
config,
20
22
headers : HeaderMap :: new ( ) ,
21
23
} ;
22
- api. login ( ) ;
24
+ api. login ( ) . await ;
23
25
api
24
26
}
25
27
@@ -34,7 +36,8 @@ pub struct ApiConnection {
34
36
impl ApiConnection {
35
37
/// Use tenant_id, client_id and secret_key to request a bearer token and store it in
36
38
/// our headers. Must be called once before requesting any content.
37
- fn login ( & mut self ) {
39
+ async fn login ( & mut self ) {
40
+ info ! ( "Logging in to Office Management API." ) ;
38
41
let auth_url = format ! ( "https://login.microsoftonline.com/{}/oauth2/token" ,
39
42
self . args. tenant_id. to_string( ) ) ;
40
43
@@ -48,43 +51,113 @@ impl ApiConnection {
48
51
49
52
self . headers . insert ( CONTENT_TYPE , "application/x-www-form-urlencoded" . parse ( ) . unwrap ( ) ) ;
50
53
51
- let login_client = reqwest:: blocking :: Client :: new ( ) ;
52
- let json : AuthResult = login_client
54
+ let login_client = reqwest:: Client :: new ( ) ;
55
+ let result = login_client
53
56
. post ( auth_url)
54
57
. headers ( self . headers . clone ( ) )
55
58
. form ( & params)
56
59
. send ( )
57
- . unwrap_or_else ( |e| panic ! ( "Could not send API login request: {}" , e) )
58
- . json ( )
59
- . unwrap_or_else ( |e| panic ! ( "Could not parse API login reply: {}" , e) ) ;
60
+ . await ;
61
+ let response = match result {
62
+ Ok ( response) => response,
63
+ Err ( e) => {
64
+ let msg = format ! ( "Could not send API login request: {}" , e) ;
65
+ error ! ( "{}" , msg) ;
66
+ panic ! ( "{}" , msg) ;
67
+ }
68
+ } ;
69
+ if !response. status ( ) . is_success ( ) {
70
+ let text = match response. text ( ) . await {
71
+ Ok ( text) => text,
72
+ Err ( e) => {
73
+ let msg = format ! ( "Received error response to API login, but could not parse response: {}" , e) ;
74
+ error ! ( "{}" , msg) ;
75
+ panic ! ( "{}" , msg) ;
76
+ }
77
+ } ;
78
+ let msg = format ! ( "Received error response to API login: {}" , text) ;
79
+ error ! ( "{}" , msg) ;
80
+ panic ! ( "{}" , msg) ;
81
+ }
82
+ let json = match response. json :: < AuthResult > ( ) . await {
83
+ Ok ( json) => json,
84
+ Err ( e) => {
85
+ let msg = format ! ( "Could not parse API login reply: {}" , e) ;
86
+ error ! ( "{}" , msg) ;
87
+ panic ! ( "{}" , msg) ;
88
+ }
89
+ } ;
60
90
61
91
let token = format ! ( "bearer {}" , json. access_token) ;
62
92
self . headers . insert ( AUTHORIZATION , token. parse ( ) . unwrap ( ) ) ;
93
+ info ! ( "Successfully logged in to Office Management API." )
63
94
}
64
95
65
96
fn get_base_url ( & self ) -> String {
66
97
format ! ( "https://manage.office.com/api/v1.0/{}/activity/feed" , self . args. tenant_id)
67
98
}
68
99
69
- pub fn subscribe_to_feeds ( & self ) {
100
+ pub async fn subscribe_to_feeds ( & self ) -> Result < ( ) > {
70
101
71
- let content_types = self . config . collect . content_types . get_content_type_strings ( ) ;
102
+ info ! ( "Subscribing to audit feeds." ) ;
103
+ let mut content_types = self . config . collect . content_types . get_content_type_strings ( ) ;
72
104
73
- let client = reqwest:: blocking:: Client :: new ( ) ;
105
+ let client = reqwest:: Client :: new ( ) ;
106
+ info ! ( "Getting current audit feed subscriptions." ) ;
107
+ let url = format ! ( "{}/subscriptions/list" , self . get_base_url( ) ) ;
108
+ let result: Vec < HashMap < String , Value > > = client
109
+ . get ( url)
110
+ . headers ( self . headers . clone ( ) )
111
+ . header ( "content-length" , 0 )
112
+ . send ( )
113
+ . await ?
114
+ . json ( )
115
+ . await ?;
116
+ for subscription in result {
117
+ let status = subscription
118
+ . get ( "status" )
119
+ . expect ( "No status in JSON" )
120
+ . as_str ( )
121
+ . unwrap ( )
122
+ . to_string ( )
123
+ . to_lowercase ( ) ;
124
+ if status == "enabled" {
125
+ let content_type = subscription
126
+ . get ( "contentType" )
127
+ . expect ( "No contentType in JSON" )
128
+ . as_str ( )
129
+ . unwrap ( )
130
+ . to_string ( )
131
+ . to_lowercase ( ) ;
132
+ if let Some ( i) = content_types
133
+ . iter ( )
134
+ . position ( |x| x. to_lowercase ( ) == content_type) {
135
+ info ! ( "Already subscribed to feed {}" , content_type) ;
136
+ content_types. remove ( i) ;
137
+ }
138
+ }
139
+ }
74
140
for content_type in content_types {
75
141
let url = format ! ( "{}/subscriptions/start?contentType={}" ,
76
142
self . get_base_url( ) ,
77
143
content_type
78
144
) ;
79
- client
145
+ debug ! ( "Subscribing to {} feed." , content_type) ;
146
+ let response = client
80
147
. post ( url)
81
148
. headers ( self . headers . clone ( ) )
82
149
. header ( "content-length" , 0 )
83
150
. send ( )
84
- . unwrap_or_else (
85
- |e| panic ! ( "Error setting feed subscription status {}" , e)
86
- ) ;
151
+ . await ?;
152
+ if !response. status ( ) . is_success ( ) {
153
+ let text = response. text ( ) . await ?;
154
+ let msg = format ! ( "Received error response subscribing to audit feed {}: {}" , content_type, text) ;
155
+ error ! ( "{}" , msg) ;
156
+ panic ! ( "{}" , msg) ;
157
+ }
87
158
}
159
+ info ! ( "All audit feeds subscriptions exist." ) ;
160
+ Ok ( ( ) )
88
161
}
89
162
90
163
0 commit comments