I've been successfully getting the list of mails in inbox using microsoft graph rest api but i'm having tough time to understand documentation on how to download attachments from mail.
For example : This question stackoverflow answer speaks about what i intend to achieve but i don't understand what is message_id in the endpoint mentioned : https://outlook.office.com/api/v2.0/me/messages/{message_id}/attachments
UPDATE
i was able to get the details of attachment using following endpoint : https://graph.microsoft.com/v1.0/me/messages/{id}/attachments and got the following response.
I was under an impression that response would probably contain link to download the attachment, however the response contains key called contentBytes which i guess is the encrypted content of file.
For attachment resource of file type contentBytes property returns
base64-encoded contents of the file
Example
The following Node.js example demonstrates how to get attachment properties along with attachment content (there is a dependency to request library):
const attachment = await getAttachment(
userId,
mesasageId,
attachmentId,
accessToken
);
const fileContent = new Buffer(attachment.contentBytes, 'base64');
//...
where
const requestAsync = options => {
return new Promise((resolve, reject) => {
request(options, (error, res, body) => {
if (!error && res.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
});
});
};
const getAttachment = (userId, messageId, attachmentId, accessToken) => {
return requestAsync({
url: `https://graph.microsoft.com/v1.0/users/${userId}/messages/${messageId}/attachments/${attachmentId}`,
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json;odata.metadata=none"
}
}).then(data => {
return JSON.parse(data);
});
};
Update
The following example demonstrates how to download attachment as a file in a browser
try {
const attachment = await getAttachment(
userId,
mesasageId,
attachmentId,
accessToken
);
download("data:application/pdf;base64," + attachment.contentBytes, "Sample.pdf","application/pdf");
} catch (ex) {
console.log(ex);
}
where
async function getAttachment(userId, messageId, attachmentId, accessToken){
const res = await fetch(
`https://graph.microsoft.com/v1.0/users/${userId}/messages/${messageId}/attachments/${attachmentId}`,
{
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json;odata.metadata=none"
}
}
);
return res.json();
}
Dependency: download.js library
I don't know if this would help but you just have to add /$value at the end of your request :
https://graph.microsoft.com/v1.0/me/messages/{message_id}/attachments/{attachment_id}/$value
Related
I cannot figure out why Axios is changing my request's content-type on retry.
I am creating an axios instance as follows (notice global default header):
import axios, { type AxiosInstance } from "axios";
const api: AxiosInstance = axios.create({
baseURL: "https://localhost:44316/",
});
export default api;
I import this instance in various components within my vue3 app. When my token has expired and I detect a 401, I use the interceptor to refresh my token and retry the call as follows (using a wait pattern to queue multiple requests and prevent requesting multiple refresh tokens):
axios.interceptors.request.use(
(config) => {
const authStore = useAuthStore();
if (!authStore.loggedIn) {
authStore.setUserFromStorage();
if (!authStore.loggedIn) {
return config;
}
}
if (config?.headers && authStore.user.accessToken) {
config.headers = {
Authorization: `Bearer ${authStore.user.accessToken}`,
};
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(res) => {
return res;
},
async (err) => {
if (err.response.status === 401 && !err.config._retry) {
console.log("new token required");
err.config._retry = true;
const authStore = useAuthStore();
if (!authStore.isRefreshing) {
authStore.isRefreshing = true;
return new Promise((resolve, reject) => {
console.log("refreshing token");
axios
.post("auth/refreshToken", {
token: authStore.user?.refreshToken,
})
.then((res) => {
authStore.setUserInfo(res.data as User);
console.log("refresh token received", err.config, res.data);
resolve(axios(err.config));
})
.catch(() => {
console.log("refresh token ERROR");
authStore.logout();
})
.finally(() => {
authStore.isRefreshing = false;
});
});
} else {
// not the first request, wait for first request to finish
return new Promise((resolve, reject) => {
const intervalId = setInterval(() => {
console.log("refresh token - waiting");
if (!authStore.isRefreshing) {
clearInterval(intervalId);
console.log("refresh token - waiting resolved", err.config);
resolve(axios(err.config));
}
}, 100);
});
}
}
return Promise.reject(err);
}
);
But when axios retries the post request, it changes the content-type:
versus the original request (with content-type application/json)
I've read every post/example I could possible find with no luck, I am relatively new to axios and any guidance/examples/documentation is greatly appreciated, I'm against the wall.
To clarify, I used this pattern because it was the most complete example I was able to put together using many different sources, I would appreciate if someone had a better pattern.
Here's your problem...
config.headers = {
Authorization: `Bearer ${authStore.user.accessToken}`,
};
You're completely overwriting the headers object in your request interceptor, leaving it bereft of everything other than Authorization.
Because the replayed err.config has already serialised the request body into a string, removing the previously calculated content-type header means the client has to infer a plain string type.
What you should do instead is directly set the new header value without overwriting the entire object.
config.headers.Authorization = `Bearer ${authStore.user.accessToken}`;
See this answer for an approach to queuing requests behind an in-progress (re)authentication request that doesn't involve intervals or timeouts.
How to get access token in SignalR package?
I get access token doing POST request and after that I get the access token. I have a model where I have parsed JSON and have token field.
Auth authFromJson(String str) => Auth.fromJson(json.decode(str));
String authToJson(Auth data) => json.encode(data.toJson());
class Auth {
Auth({
this.token,
this.user,
});
final String? token;
final User? user;
POST request to API to get accesss token which I got succesfully:
Future<Auth> getToken() async {
String _email = "admin";
String _password = "admin";
Map<String, String> headers = {
'Content-Type': 'application/json',
'accept': ' */*'
};
final body = {
'username': _email,
'password': _password,
};
var response = await http.post(
Uri.parse("http://******/login"),
headers: headers,
body: jsonEncode(body),
);
print(response.body);
print(response.statusCode);
var jsonResponse = jsonDecode(response.body);
return Auth.fromJson(jsonResponse);
}
What I have in print in my console:
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA","user":{}}
After all this stuff I opened docs and found out how SignalR package handle token auth and did the same thing:
Future<List> fetchLists() async {
final httpConnectionOptions = HttpConnectionOptions(
accessTokenFactory: () => getToken().then((value) => value.token ?? ''),
);
final hubConnection = HubConnectionBuilder()
.withUrl('http://*****/hub',
options: httpConnectionOptions)
.build();
await hubConnection.start();
So after all of this I got this error [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: 302: Found
it means what I should add access token to each of requests and I do, but still get this error. How can i solve it or may be there is anoher way to add token in HubConnectionBuild?
There is parameter in accessTokenFactory which accept a function and have return type String so make a function which return token .
below attached code for your reference-
_hubConnection = HubConnectionBuilder()
.withUrl(chaturl,
options: HttpConnectionOptions(
headers: defaultHeaders,
accessTokenFactory: () async => await getToken() //define a function which return token
))
withAutomaticReconnect(retryDelays: [
20000,
]
).build();
//get token method
Future<dynamic> getToken() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
if (sharedPreferences.containsKey("token")) {
print(sharedPreferences.getString("token"));
return sharedPreferences.getString("token");
} else {
return null;
}
}
I'm trying to pull data from Hubspot into Pendo using Zapier code (a recommendation from my Pendo rep). When testing using the code below I get "Syntax error: await is only valid in async function".
I have researched and tried making an async IIFE but that also didn't work. So, I'm wondering if there is an error somewhere else in my code causing the error, or, is there a better way to approach this rather than using await?
const data = [{
"accountId": inputData.body.accountId,
"values": {
"Became Customer": inputData.body.becameCustomer,
"Total MRR": inputData.body.totalMRR,
"Company Owner": inputData.body.companyOwner
}
}];
function updateAccount (z, bundle) {
const promise = await fetch("https://app.pendo.io/api/v1/metadata/account/agent/value", {
method: "POST",
body: JSON.stringify(data),
headers: {
"content-type": "application/json",
"x-pendo-integration-key": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.XX"}
});
return promise.then((response) => {
if (response.status != 200) {
throw new Error(`Unexpected status code ${response.status}`);
} else {
const content = JSON.parse(response.content);
return content;
}
});
}
updateAccount()```
Wrapping the function in async got rid of the error.
const updateAccount = async function(z, bundle) {
I'm trying to add Cookies to my request:
Here i get csrftoken with a GET request:
Future<String> getCsrftoken() async{
var response = await http.get(Uri.encodeFull('http://test/accounts/login/'));
var csrftoken = response.headers.remove('set-cookie').substring(10,74); //csrf
64 chars
return csrftoken;
}
Here i'm trying to perform the POST (application/x-www-form-urlencoded) request using the package Dio.
getSessionId() async {
var csrf = await getCsrftoken();
var cj = new CookieJar();
List<Cookie> cookies = [new Cookie("csrftoken", csrf)];
cj.saveFromResponse(Uri.parse("http://test/accounts/login/"), cookies);
List<Cookie> results = cj.loadForRequest(Uri.parse("http://test/accounts/login/"));
var dio = new Dio(new Options(
baseUrl: "http://test/accounts/login/",
connectTimeout: 5000,
receiveTimeout: 100000,
// 5s
headers: {
},
contentType: ContentType.JSON,
// Transform the response data to a String encoded with UTF8.
// The default value is [ResponseType.JSON].
responseType: ResponseType.PLAIN
));
Response<String> response;
response = await dio.post("",
data: {
"username": "username",
"password": "password",
"csrfmiddlewaretoken" : getCsrftoken()
},
// Send data with "application/x-www-form-urlencoded" format
options: new Options(
contentType: ContentType.parse("application/x-www-form-urlencoded")),
);
print(response.statusCode);
}
I get 403 status code, because i need to add as a cookie csrftoken.
How should I proceed?
From the Dio Dart API Docs:
Cookie Manager
You can manage the request/response cookies using cookieJar .
The dio cookie manage API is based on the withdrawn cookie_jar.
You can create a CookieJar or PersistCookieJar to manage cookies automatically, and dio use the CookieJar by default, which saves the cookies in RAM. If you want to persists cookies, you can use the PersistCookieJar class, the example codes as follows:
var dio = new Dio();
dio.cookieJar=new PersistCookieJar("./cookies");
PersistCookieJar is a cookie manager which implements the standard cookie policy declared in RFC. PersistCookieJar persists the cookies in files, so if the application exit, the cookies always exist unless call delete explicitly.
More details about cookie_jar see : https://github.com/flutterchina/cookie_jar .
Check if the csrftoken needs to be passed in the header and the cookie or just one of them. It sometimes needs to be included as a header, which is shown in the example below, but the header name varies. To persist cookies, use a PersistCookieJar. Other options are persisted through BaseOptions (previously named Options).
Add to pubspec.yaml the latest versions of these plugins
path_provider: ^1.1.0
dio: ^2.1.6
cookie_jar: ^1.0.0
In a new class named webFunctions:
import 'dart:io';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';
class webFunctions {
final Dio _dio = Dio();
PersistCookieJar persistentCookies;
final String URL = "http://test/";
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<Directory> get _localCoookieDirectory async {
final path = await _localPath;
final Directory dir = new Directory('$path/cookies');
await dir.create();
return dir;
}
Future<String> getCsrftoken() async{
try {
String csrfTokenValue;
final Directory dir = await _localCoookieDirectory;
final cookiePath = dir.path;
persistentCookies = new PersistCookieJar(dir: '$cookiePath');
persistentCookies.deleteAll(); //clearing any existing cookies for a fresh start
_dio.interceptors.add(
CookieManager(persistentCookies) //this sets up _dio to persist cookies throughout subsequent requests
);
_dio.options = new BaseOptions(
baseUrl: URL,
contentType: ContentType.json,
responseType: ResponseType.plain,
connectTimeout: 5000,
receiveTimeout: 100000,
headers: {
HttpHeaders.userAgentHeader: "dio",
"Connection": "keep-alive",
},
); //BaseOptions will be persisted throughout subsequent requests made with _dio
_dio.interceptors.add(
InterceptorsWrapper(
onResponse:(Response response) {
List<Cookie> cookies = persistentCookies.loadForRequest(Uri.parse(URL));
csrfTokenValue = cookies.firstWhere((c) => c.name == 'csrftoken', orElse: () => null)?.value;
if (csrfTokenValue != null) {
_dio.options.headers['X-CSRF-TOKEN'] = csrfTokenValue; //setting the csrftoken from the response in the headers
}
return response;
}
)
);
await _dio.get("/accounts/login/");
return csrfTokenValue;
} catch (error, stacktrace) {
print("Exception occured: $error stackTrace: $stacktrace");
return null;
}
}
getSessionId() async {
try {
final csrf = await getCsrftoken();
FormData formData = new FormData.from({
"username": "username",
"password": 'A *passphrase* is stronger than a password.',
"csrfmiddlewaretoken" : '$csrf'
});
Options optionData = new Options(
contentType: ContentType.parse("application/x-www-form-urlencoded"),
);
Response response = await _dio.post("/accounts/login/", data: formData, options: optionData);
print(response.statusCode);
} on DioError catch(e) {
if(e.response != null) {
print( e.response.statusCode.toString() + " " + e.response.statusMessage);
print(e.response.data);
print(e.response.headers);
print(e.response.request);
} else{
print(e.request);
print(e.message);
}
}
catch (error, stacktrace) {
print("Exception occured: $error stackTrace: $stacktrace");
return null;
}
}
}
I'd like to send two files to http post
curl looks like this
curl -X POST "https://api-us.faceplusplus.com/facepp/v3/compare" \
-F "api_key=<api_key>" \
-F "api_secret=<api_secret>" \
-F "image_file1 =file1" \
-F "image_file1 =file2"
I tried like this.
File first;
File second;
var uri = Uri.parse('https://api-us.faceplusplus.com/facepp/v3/compare');
var request = new http.MultipartRequest("POST", uri);
request.fields['api_key'] = apiKey;
request.fields['api_secret'] = apiSecret;
request.files.add(await http.MultipartFile.fromPath('image_file1', first.path, contentType: new MediaType('application', 'x-tar')));
request.files.add(await http.MultipartFile.fromPath('image_file2', second.path, contentType: new MediaType('application', 'x-tar')));
var response = await request.send();
print(response);
But it returns this
NoSuchMethodError: Class 'String' has no instance getter 'path'.
How can I send these properly?
It doesn't look like first and second are actually Files. When they are definitely files, as in the following example, I get 401 (as expected, as I have a dummy api key).
main() async {
File first = File('pubspec.yaml');
File second = File('analysis_options.yaml');
Uri uri = Uri.parse('https://api-us.faceplusplus.com/facepp/v3/compare');
http.MultipartRequest request = new http.MultipartRequest('POST', uri);
request.fields['api_key'] = 'apiKey';
request.fields['api_secret'] = 'apiSecret';
request.files.add(await http.MultipartFile.fromPath('image_file1', first.path,
contentType: new MediaType('application', 'x-tar')));
request.files.add(await http.MultipartFile.fromPath(
'image_file2', second.path,
contentType: new MediaType('application', 'x-tar')));
http.StreamedResponse response = await request.send();
print(response.statusCode);
}
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:path/path.dart';
class Post {
String title = '';
String description = '';
List<File> files = [];
Future<http.StreamedResponse> sendFiles() async {
try {
var uri = Uri.parse('https://example.com/api-endpoint');
var request = http.MultipartRequest('POST', uri);
// Headers
request.headers.addAll({
'Accept': 'application/json',
'Authorization': 'Bearer $token',
});
// Fields
request.fields['title'] = title;
request.fields['description'] = description;
// Files
await Future.forEach(
files,
(file) async => {
request.files.add(
http.MultipartFile(
'files',
(http.ByteStream(file.openRead())).cast(),
await file.length(),
filename: basename(file.path),
),
)
},
);
return await request.send();
} catch (err) {
print(err);
return null;
}
}
}
You can try with Flutter DIO Package
https://pub.dev/packages/dio
import 'package:dio/dio.dart';
// Create a Dio instance
final Dio dio = Dio();
// Set up the request options
Options options = Options(
method: 'POST',
);
// Create a FormData instance to hold the files you want to upload
FormData formData = FormData();
// Add the files to the FormData instance
formData.files.addAll([
MapEntry('file1', await MultipartFile.fromFile(file1Path)),
MapEntry('file2', await MultipartFile.fromFile(file2Path)),
// Add more files here if you want
]);
try {
// Send the POST request
Response response = await dio.post(
url,
data: formData,
options: options,
);
// Do something with the response
print(response.data);
} catch (e) {
// show my error
log(e);
}