I have a problem when send data from client that wrote by python to aws lambda. After that AWS Lambda send it to slack. Under is my code, I want to display "Message sent by raspi 1" but it displayed "Message sent by raspi undefined" in slack message.
Python file:
import requests
import json
url = 'https://.....execute-api.us-west-2.amazonaws.com/product/events'
data1 = json.dumps({'a': 1, 'b': 2})
x = requests.post(url, data = data1)
Lambda fuction:
var aws = require('aws-sdk');
const https = require('https');
exports.handler = (event, context, callback) => {
var responseBody = {
"key3": "value3",
"key2": "value2",
"key1": "value1"
};
var response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(responseBody),
"isBase64Encoded": false
};
const payload = JSON.stringify({
text: `Message sent by raspi ${event.body.a}`,
});
const options = {
hostname: "hooks.slack.com",
method: "POST",
path: "https://hooks.slack.com/services/TPA2SP0GH/....",
};
const req = https.request(options,
(res) => res.on("data", () => callback(null, response)))
req.on("error", (error) => callback(JSON.stringify(error)));
req.write(payload);
req.end();
};
the event.body is a json string. you need to parse it before use it.
here:
const parsedBody = JSON.parse(event.body);
const payload = JSON.stringify({
text: `Message sent by raspi ${parsedBody.a}`,
});
Hope this helps.
Try accessing it with ${event['a']}. It is still a dictionary, not yet converted to an object.
Related
Been trying for days to implement login with the /token endpoint from fastAPI in my flutter app I have checked many tutorials online but none seems to show how to do it correctly when using JWT and OAuth. I decided to go with Dio uisng fullstacks tutorial on youtube i was able to understand Dio and implement it in my app. But i get this error in flutter when i hit the login button:
error: {detail: [{loc: [body, username], msg: field required, type: value_error.missing},
And this error on fastAPI:
"POST /token HTTP/1.1" 422 Unprocessable Entity
My flutter login.dart looks like this:
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import '../../dio/dio_client.dart';
import '../../dio/dio_token_manager.dart';
import 'logout.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
final _controllerUsername = TextEditingController();
final _controllerPassword = TextEditingController();
var _username = '', _password = '';
#override
void initState() {
super.initState();
() async {
DioClient.dio.interceptors.add(TokenManager.instance);
await checkLogin();
}();
}
Future<void> checkLogin() async {
try {
var response = await DioClient.dio.get('http://10.0.2.2:8000/token');
if (response.statusCode == 200) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => const LogoutScreen()));
}
} on DioError catch (e) {
debugPrint("Status code: ${e.response?.statusCode}");
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
decoration: const InputDecoration(labelText: 'Username'),
validator: (val) => val!.isEmpty ? 'Username Required' : null,
onSaved: (val) => _username = val!,
keyboardType: TextInputType.text,
controller: _controllerUsername,
autocorrect: false,
),
TextFormField(
decoration: const InputDecoration(labelText: 'Password'),
obscureText: true,
validator: (val) => val!.isEmpty ? 'Password Required' : null,
onSaved: (val) => _password = val!,
controller: _controllerPassword,
keyboardType: TextInputType.text,
autocorrect: false,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
final form = _formKey.currentState;
if (form!.validate()) {
form.save();
final snackbar = SnackBar(
duration: const Duration(seconds: 30),
content: Row(
children: const [
CircularProgressIndicator(),
Text(" Logging In...")
],
),
);
ScaffoldMessenger.of(context).showSnackBar(snackbar);
await Future.delayed(const Duration(seconds: 2));
try {
final body = {
"username": _username,
"password": _password,
};
var response = await DioClient.dio.post(
'http://10.0.2.2:8000/token',
//data: jsonEncode(body),
data: {
"username": _username,
"password": _password,
},
);
if (response.statusCode == 200) {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => const LogoutScreen()));
}
} on DioError catch (e) {
debugPrint("error: ${e.response?.data}");
}
ScaffoldMessenger.of(context).hideCurrentSnackBar();
}
},
child: const Text('Log In'),
)
],
),
),
),
);
}
}
From the fastAPI localhost:8000/docs, the /token endpoint returns the 200 OK response if you are testing the API on insomnia or postman but does not work when you try to implement the same logic on a frontend app like flutter. The fastAPI return the json with an access_token:
{
"access_token": "string",
"token_type": "string"
}
The code for the fastAPI endpoints comes from this default FastAPI Docs with username as johndoe
For the Dio_token_manager.dart :
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
class TokenManager extends Interceptor {
static final TokenManager _instance = TokenManager._internal();
static TokenManager get instance => _instance;
TokenManager._internal();
String? _token;
#override
void onResponse(
Response response,
ResponseInterceptorHandler handler,
) {
if (response.statusCode == 200) {
var data = Map<String, dynamic>.from(response.data);
if (data['set-token'] != null) {
saveToken(data['token']);
}
} else if (response.statusCode == 401) {
clearToken();
}
super.onResponse(response, handler);
}
#override
void onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) {
options.headers['Token'] = _token;
return super.onRequest(options, handler);
}
Future<void> initToken() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_token = prefs.getString('token');
}
void saveToken(String newToken) async {
debugPrint('new token $newToken');
if (_token != newToken) {
_token = newToken;
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('token', _token!);
}
}
So for the dio_client.dart
class DioClient {
static Dio dio = Dio();
static const baseUrl = "http://10.0.2.2:8000";
static const productsEndpoint = "$baseUrl/products";}
What i have tried so far:
I tried adding the jsonEncode to the data as can be seen from the commented code in the login code.
I tried adding headers to the dioClient.post but it still doesnt work.
The fastAPI login route:
#app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
The backend works just fine i think. Maybe there is something i am not doing right. Please help. Thank you.
OAuth2PasswordRequestForm expects its data as POST form variables, not as JSON.
You can change the format sent by referencing the example given in the dio documentation for how to "Send form data" instead:
var formData = FormData.fromMap({
'name': 'wendux',
'age': 25,
});
var response = await dio.post('/info', data: formData);
In your case this would reference the username/password variables instead:
final body = FormData.fromMap({
"username": _username,
"password": _password,
});
var response = await DioClient.dio.post(
'http://10.0.2.2:8000/token',
data: body,
);
I was trying to follow this tutorial https://marmelab.com/react-admin/Tutorial.html#connecting-to-a-real-api
my server is a simple python flask as endpoint server, as the tutorial said, i need set x-total-count into header, so i do it in my flask app like:
#posts.get("/posts")
def all():
print("headers-->", request.headers)
payload = [
{
"userId": 10,
"id": 100,
"title": "at nam consequatur ea labore ea harum",
"body": "cupiditate quo est a modi nesciunt soluta\nipsa voluptas error itaque dicta in\nautem qui minus magnam et distinctio eum\naccusamus ratione error aut",
"views": 1999,
"published_at": "12/10/2022"
}
]
# X-Total-Count
headers = {"X-Total-Count": "20", "Access-Control-Expose-Headers": "x-total-count"}
response = jsonify(payload)
return response, HTTP_200_OK, headers
and after i do curl or refresh my browser, i can see the headers has been sent to client,
and my reactjs code like below:
import { fetchUtils } from "react-admin";
import { stringify} from "query-string";
const apiUrl = "http://192.168.31.40:5001/posts"
// const apiUrl ="https://jsonplaceholder.typicode.com";
// const httpClient = fetchUtils.fetchJson;
const authToken="123123";
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json', "Access-Control-Expose-Headers": "X-Total-Count"});
}
options.headers.set('Authorization', authToken);
// options.headers.set(Access-Control-Expose-Headers, "*");
// Access-Control-Expose-Headers
return fetchUtils.fetchJson(apiUrl, options)
};
export default {
// 获取列表
getList: async (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify(params.filter),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
const {status, headers, body, json } = await httpClient(url);
headers.set("a", "123")
console.log("status --->", status)
console.log("headers --->", headers)
console.log("body --->", body)
return ({
data: json,
total: 1,//parseInt(headers.get('x-total-count')),
});
},
getOne: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`).then(({ json }) => ({
data: json,
})),
getMany: async (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids }),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
const { json } = await httpClient(url);
return ({ data: json });
},
getManyReference: async (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify({
...params.filter,
[params.target]: params.id,
}),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
const { headers, json } = await httpClient(url);
return ({
data: json,
total: parseInt(headers.get('content-range').split('/').pop(), 10),
});
},
update: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'PUT',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json })),
updateMany: async (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids}),
};
const { json } = await httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'PUT',
body: JSON.stringify(params.data),
});
return ({ data: json });
},
create: (resource, params) =>
httpClient(`${apiUrl}/${resource}`, {
method: 'POST',
body: JSON.stringify(params.data),
}).then(({ json }) => ({
data: { ...params.data, id: json.id },
})),
delete: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'DELETE',
}).then(({ json }) => ({ data: json })),
deleteMany: async (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids}),
};
const { json } = await httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'DELETE',
});
return ({ data: json });
}
};
I can't see the headers in browser console.
In the browser inspector the headers look like they are empty because it's an iterator, not an object. You can iterate through the response headers like so:
for (var pair of response.headers.entries()) {
console.log(pair[0]+ ': '+ pair[1]);
}
Check out this blogpost for more information: https://stevemiller.dev/2019/getting-response-headers-with-javascript-fetch/
I also had to explicitly expose my custom header in my web api backend for it to work. This might also be a reason why the response header is not showing up.
While calling the python API from angular 10. I am getting multiple response. How to overcome it.
Python API Response from Angular Service call:
1. Object { type: 0 } # how to avoid this additional response
2. Object { headers: {…}, status: 201, statusText: "Created", url: "https://*********/1bcd2a20-dd60-4cfe-984f-7f3607f01e7c", ok: true, type: 4, body: "{\"status\":{}}" }
Angular Service Call:
postCall(identifier): Observable<HttpEvent<{}>> {
let token1 = localStorage.getItem('token');
const headerSettings: { [name: string]: string | string[]; } = {};
headerSettings['Authorization'] = 'Bearer ' + token1;
const req = new HttpRequest('GET', 'https://***********/'+identifier, null, {
reportProgress: false,
responseType: 'text'
});
const newHeader = new HttpHeaders(headerSettings);
let changedRequest = req.clone({
headers: newHeader
});
return this.http.request(changedRequest);
}
I am trying to give an image a predicted label by a trained AutoML model in Firebase function. This image is stored at Google Cloud Storage.
I tried to read the image in this way:
const gcs = require('#google-cloud/storage')();
const myBucket = gcs.bucket(object.bucket);
const file = myBucket.file(object.name);
const stream = file.createReadStream();
var data = '';
stream.on('error', function(err) {
console.log("error");
})
.on('data', function(chunk) {
data = data + chunk;
console.log("Writing data");
})
.on('end', function() {
});
After I finished reading data, I transfer the data into 'binary' format
var encoded = new Buffer(data)
encoded = encoded.toString('binary');
But I feed these encoded data into 'imageBytes':
const payload = {
"image": {
"imageBytes": encoded
},
};
var formattedName = client.modelPath(project, location, model);
var request = {
name: formattedName,
payload: payload,
};
client.predict(request)
.then(responses => {
console.log("responses:", responses);
var response = responses[0];
console.log("response:", response);
})
.catch(err => {
console.error(err);
});
It will throw an error:
Error: invalid encoding
at Error (native)
at Object.decode (/user_code/node_modules/#google-cloud/automl/node_modules/#protobufjs/base64/index.js:105:19)
at Type.Image$fromObject [as fromObject] (eval at Codegen (/user_code/node_modules/#google-cloud/automl/node_modules/#protobufjs/codegen/index.js:50:33), <anonymous>:9:15)
at Type.ExamplePayload$fromObject [as fromObject] (eval at Codegen (/user_code/node_modules/#google-cloud/automl/node_modules/#protobufjs/codegen/index.js:50:33), <anonymous>:10:20)
at Type.PredictRequest$fromObject [as fromObject] (eval at Codegen (/user_code/node_modules/#google-cloud/automl/node_modules/#protobufjs/codegen/index.js:50:33), <anonymous>:13:22)
at serialize (/user_code/node_modules/#google-cloud/automl/node_modules/grpc/src/protobuf_js_6_common.js:70:23)
at Object.final_requester.sendMessage (/user_code/node_modules/#google-cloud/automl/node_modules/grpc/src/client_interceptors.js:802:37)
at InterceptingCall._callNext (/user_code/node_modules/#google-cloud/automl/node_modules/grpc/src/client_interceptors.js:418:43)
at InterceptingCall.sendMessage (/user_code/node_modules/#google-cloud/automl/node_modules/grpc/src/client_interceptors.js:460:8)
at InterceptingCall._callNext (/user_code/node_modules/#google-cloud/automl/node_modules/grpc/src/client_interceptors.js:424:12)
But if I encoded the image in 'base64', it will throw an error:
Error: 3 INVALID_ARGUMENT: Provided image is not valid.
at Object.exports.createStatusError (/user_code/node_modules/#google-cloud/automl/node_modules/grpc/src/common.js:87:15)
at Object.onReceiveStatus (/user_code/node_modules/#google-cloud/automl/node_modules/grpc/src/client_interceptors.js:1188:28)
at InterceptingListener._callNext (/user_code/node_modules/#google-cloud/automl/node_modules/grpc/src/client_interceptors.js:564:42)
at InterceptingListener.onReceiveStatus (/user_code/node_modules/#google-cloud/automl/node_modules/grpc/src/client_interceptors.js:614:8)
at callback (/user_code/node_modules/#google-cloud/automl/node_modules/grpc/src/client_interceptors.js:841:24)
code: 3,
metadata: Metadata { _internal_repr: { 'grpc-server-stats-bin': [Object] } },
details: 'Provided image is not valid.'
I tried local image file prediction in Python as well, it uses 'binary' binary representation and it works well. When I use 'base64' in Python it will return "Provided image is not valid." as in firebase function.
I am confusing that whether I read the image from Cloud Storage in a wrong way or I encoded the image in a wrong way.
Complete Code in Firebase function:
const automl = require('#google-cloud/automl');
var client = new automl.v1beta1.PredictionServiceClient();
const gcs = require('#google-cloud/storage')();
const myBucket = gcs.bucket(object.bucket);
const file = myBucket.file(object.name);
const stream = file.createReadStream();
var data = '';
stream.on('error', function(err) {
console.log("error");
})
.on('data', function(chunk) {
data = data + chunk;
console.log("Writing data");
})
.on('end', function() {
var encoded = new Buffer(data)
encoded = encoded.toString('binary');
console.log("binary:", encoded);
const payload = {
"image": {
"imageBytes": encoded
},
};
var formattedName = client.modelPath(project, location, model);
var request = {
name: formattedName,
payload: payload,
};
client.predict(request)
.then(responses => {
console.log("responses:", responses);
var response = responses[0];
console.log("response:", response);
})
.catch(err => {
console.error(err);
});
stream.destroy();
});
Complete code in Python:
import sys
from google.cloud import automl_v1beta1
from google.cloud.automl_v1beta1.proto import service_pb2
# Import the base64 encoding library.
import base64
def get_prediction(content, project_id, model_id):
prediction_client = automl_v1beta1.PredictionServiceClient()
name = 'projects/{}/locations/us-central1/models/{}'.format(project_id, model_id)
payload = {'image': {'image_bytes': content }}
params = {}
request = prediction_client.predict(name, payload, params)
return request # waits till request is returned
if __name__ == '__main__':
file_path = sys.argv[1]
project_id = sys.argv[2]
model_id = sys.argv[3]
with open(file_path, 'rb') as ff:
content = ff.read()
print(content)
# Encoded as base64
# content = base64.b64encode(content)
print(get_prediction(content, project_id, model_id))
I use file.download(), it works.
file.download().then(imageData => {
const image = imageData[0];
const buffer = image.toString('base64');
const payload = {
"image": {
"imageBytes": buffer
}
}
const request = {
name: formattedName,
payload: payload
};
client.predict(request).then(result => {
console.log('predict:', result);
}).catch(err => console.error(err));
});
Angularjs code
var app = angular.module('myApp', []);
app.factory('httpSend', ['$http', '$q', function($http, $q) {
var app = {};
app.sendToServer = function(data) {
$http({
method: "POST",
url: '/report',
data: data,
headers: {
'Content-type': 'application/x-www-form.urlencoded;'
}
}).then(function(response) {
debugger
var result = data;
});
}
app.getfromServer = function() {
var def = $q.defer();
$http.get('/report').then(function(data) {
console.log(data);
def.resolve(data);
}),
function(error) {
def.reject("Failed to get albums");
};
return def.promise;
}
return app;
}]);
app.controller('myCtrl', ['$scope', '$http', 'httpSend', '$filter', function($scope, $http, httpSend, $filter) {
$scope.names = ["ankit patidar", "adhishi ahari", "kritin joshi", "kautilya bharadwaj", "punita ojha", "manvi agarwal", "apeksha purohit", "shipra jain", "mansi nangawat", "praveen soni"];
$scope.data = [];
$scope.names.forEach(function(name) {
$scope.data.push({
name: name,
checkin: "",
checkout: ""
})
});
$scope.login = [];
$scope.check = function(name, doing) {
debugger
name[doing] = new Date();
name[doing] = $filter('date')(name[doing], 'dd-MM-yyyy hh:mm:ss');
$scope.login.push(angular.copy(name));
if (doing == "checkout") {
var q = JSON.stringify($scope.login);
httpSend.sendToServer(q);
}
}
$scope.getData = function() {
httpSend.getfromServer();
}
}]);
`
Python Code
def get(self):
logging.info('get is triggered')
obj = CheckIn.query().fetch()
emp_obj = []
for x in obj:
logging.info('I am inside for loop ')
emp_obj.append({
'name': x.name,
'Check_in': x.inDate,
'check_out': x.outDate
})
logging.info('I am inside emp_obj')
self.response.write(json.dumps(emp_obj))
i need to fetch all the data stored on ndb datastore on front end view thats why i m using http get method but error is showed method not allowed. can u please help e despite using query fetch and showing its response on python ad triggering get method, why error is coming, is there a mistake in control flow or something is missing in my get method, as for now i m able to post nd store data
Change your factory to the following. Don't use the same variable app that you are using for initialising your module for your controller logic.
app.factory('httpSend',['$http', '$q',function($http, $q){
return {
'sendToServer': function(data) {
var def = $q.defer();
$http({
method: "POST",
url: '/report',
data: data,
headers: {
'Content-Type': 'application/json'
}
}).then(function(response) {
debugger
var result = response.data;
def.resolve(result );
});
return def.promise;
},
'getfromServer': function() {
var def = $q.defer();
$http.get('/report').then(function(data) {
console.log(data);
def.resolve(data);
}),
function(error) {
def.reject("Failed to get albums");
};
return def.promise;
}
}
}]);