Symfony, Monolog and Slack : how can I format notifications? - symfony

On a Symfony 3.4 project, I want to send notifications to a Slack channel via Monolog. I use the SlackHandler and it works well but now I want to find a way to format my notification, using Slack's recommendations. Is it possible ?
For now, each key I send is a new field title and each value associated is a field. Can I directly change the content of "Message" ? Can I also hide the "Level" entry ? I try to use 'fields' from $attachment in SlackRecord.php, but no success so far.
Many, many thanks.
Extracts of what I've done so far. My yaml config:
# monolog.yml
reviews:
type: slack
token: '%token%'
channel: "#mychannel"
bot_name: 'My bot'
icon_emoji: :sports_medal:
level: info
channels: [reviews]
include_extra: true
And in my controller :
// ...
$message = [
'a' => $post->getTitle(),
'b' => $ur->getInterventionRating(),
"fields" => [
"title": "Priority",
"value": "High",
"short": false ], ];
$this->container->get('monolog.logger.myChannel')->info("test", $message);

Related

How to create api with resource not based on entity API Platform?

I need to create a GET endpoint to return a resource which is fetched from another application via http client, not based on entity. The resource I fetched is an array:
[
"id" => 1234
"first_name" => ""
"last_name" => ""
"email" => ""
"country" => 1
"country_code" => 93
"phone_number" => "3434343"
"nationality" => "AF"
"professional_industry" => "Health Care"
"job_title" => "Medical Doctor - General Practitioner"
"specialisation" => "No specialisation"
"career_length_month" => 1
"career_length_year" => 1
]
Then I need to query database to fetch some data to add to the resource array.
So I created it in src/Controller/MyController.php:
/**
* #Route("/api/v1/get-profile", name="get_profile")
* #return JsonResponse
*/
public function getMemberInfo(): JsonResponse
{
// step 1 : use http client to request data from another application
// step 2 : query DB to fetch some data and add to data array
return new JsonResponse($data, Response::HTTP_OK);
}
But now, I would like my api return json api response format : https://jsonapi.org/.
With resource based on entity, it is supported completely by api-platform. I don't need to do much. I just adding "key" #[ApiResource] in entity class and config some things. Then I have a api with json api format, so easily:
{
"data": {
"id": "",
"type": "",
"attributes": {}
}
}
But how about with resource not based on Entity? Is there any built-in feature of api-platform I could use, or I have to do a transformer by myself ?
You need override the OpenAPI specification how described in documentation:
https://api-platform.com/docs/core/openapi/#overriding-the-openapi-specification
Then, in the new class, you must add the schema definition that you require, adding new PathItem instances, each PathItem require new Operation instances, etc.
Sorry for not paste examples, it require so many code. I'm working this way in a project, but it's not public for now.
I hope I put you in the way.
The good practice is to can create a custom data provider. This custom data provider will be responsible to fetch data from the external source. Then, API-Platform will be able to use it like a regular entity.
Documentation
Full example in the API-Platform demo

WooCommerce REST API: Gateway does not support admin changing the payment method on a Subscription

I am using WooCommerce Subscription REST API to extend the subscription of the user. The problem that I have is that when I want to update expire time I get this error:
Gateway does not support admin changing the payment method on a Subscription
Does someone know where is the problem?
I am using the following code to update the subscription expire time:
return $this->guzzleClient->request('PUT', 'wp-json/wc/v1/subscriptions/'.$id, [
'json' => [
'end_date' => $endDate->toDateTimeString(),
'status' => 'active',
]
]);
We ran into a similar issue. Instead of passing the whole subscription object back, we instead only passed the necessary information (in our case we wanted to add to the coupon_lines field).
Our resulting payload looked like:
{
coupon_lines: [
{
code: "sample-code",
amount: "10.00"
}
]
}

JWT key for mercure

I try generate JWT key for Mercure settings
I use this manual
https://medium.com/#stefan.poeltl/instant-realtime-notifications-with-symfony-and-mercure-e45270f7c8a5
for pass myJWTKey JWT is
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6W10sInB1Ymxpc2giOlsiKiJdfX0.iTVjHoLv9bB-O5RNnTtzOFxIW-YECk2JXZeMekZ4GwA
I found a token builder ( Signed JSON Web Token )
http://jwtbuilder.jamiekurtz.com/
but I find no setting that generates a correct JWT. How do I do it? What I miss?
I tried generate token for env settings
MERCURE_PUBLISH_URL=http://mercure.dev:3000/.well-known/mercure
# The default token is signed with the secret key: !ChangeMe!
MERCURE_JWT_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6W10sInB1Ymxpc2giOlsiKiJdfX0.iTVjHoLv9bB-O5RNnTtzOFxIW-YECk2JXZeMekZ4GwA
###< symfony/mercure-bundle ###
This token is for default password in docker-compose
mercure:
image: dunglas/mercure
environment:
# You should definitely change all these values in production
- JWT_KEY=myJWTKey
- DEMO=1
- ALLOW_ANONYMOUS=1
- HEARTBEAT_INTERVAL=30s
- ADDR=:3000
if I change myJWTKey to mysecure pass - how I can generate token?
Just an addition to a great answer by #Daidon. Mercure bundle uses lcobucci/jwt and registers it's factory as a service.
If you want to generate JWT do the following
Pass the factory as an argument with #mercure.hub.default.jwt.factory (here default is for your hub name)
In your service/controller
public function generateJwt(LcobucciFactory $factory): string
{
return $factory->create(['*']);
}
UPD: even easier way to get a JWT token
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mercure\Authorization;
public function generateJwt(Request $request, Authorization $authorization): string
{
return $authorization->createCookie($request, ['*'])->getValue();
}
You can use different libraries for doing that, a very simple and fast one would be php-jwt
Then do
composer require firebase/php-jwt
And in the code you can do then:
use \Firebase\JWT\JWT;
$key = "12345678";
$payload = [
'mercure' => [
'publish' => ['*'],
],
];
$jwt = JWT::encode($payload, $key); // holds valid jwt now
The library will automatically inject the headers that you need (default: alg HS256, typ: jwt) and set the payload for you. Then it encodes it to base64 and signs it also.
Go on and set a cookie with this jwt or use it in authorization header now :-)
If you want to use the JWT for subscriber authentication, don't forget to put the subscribe key in the payload.
$payload = [
'mercure' => [
'subscribe' => ['*'], // make this a list of concrete topics, don't use *
],
];
Also for that usecase, you can carry around some data in the cookie, by providing a payload key with an object:
$payload = [
'mercure' => [
'subscribe' => ['*'],
'payload' => [
'userId' => $user->getId()
]
],
];
Apologize for late answer, you can simply generate new jwt token once using the official page https://jwt.io/.

How to create Firebase push notifications group in React Native?

I get a problem using Firebase to create push notifications group in React Native, when I receive many push notifications I want to create it to be 1 group like WhatsApp (like red circle in image below).
I have tried to look for many examples but I didn't found a solution. Please anyone help me to create like that.
Thanks.
use react-native-push-notification
// create channel
import PushNotification, {Importance} from 'react-native-push-notification';
...
PushNotification.createChannel(
{
channelId: "channel-id", // (required)
channelName: "My channel", // (required)
channelDescription: "A channel to categorise your notifications", // (optional) default: undefined.
playSound: false, // (optional) default: true
soundName: "default", // (optional) See `soundName` parameter of `localNotification` function
importance: Importance.HIGH, // (optional) default: Importance.HIGH. Int value of the Android notification importance
vibrate: true, // (optional) default: true. Creates the default vibration pattern if true.
},
(created) => console.log(`createChannel returned '${created}'`) // (optional) callback returns whether the channel was created, false means it already existed.
);
// this shows each new notification for a while then pushes it into group
PushNotification.localNotification({
channelId: 'channel-id',
title: `New message`,
message: 'Hello world',
})
// this groups messages
PushNotification.localNotification({
channelId: 'channel-id',
id: 'messages',
group: 'messages',
groupSummary: true
})

Adding more fields to Meteor user accounts

I am using mrt add accounts-ui-bootstrap-dropdown and mrt add accounts-password to get a simple login page running on my app.
The accounts users gives me a nice hash containing ids, createdAt, emails, etc.
If I wanted to add other fields in this hash so I can make use of them later, how would I do that? For example, I want then to also enter their given name and surname:
"given_name": "John", "surname": "Doe"
Users are special objects in meteor ; you don't want to add fields in the user but in the users profile.
From the doc :
By default the server publishes username, emails, and profile.
If you want to add properties like surname when you create the account, you should use in the Account.onCreateUser server-side hook : http://docs.meteor.com/#accounts_oncreateuser
Accounts.onCreateUser(function(options, user) {
//pass the surname in the options
user.profile['surname'] = options.surname
return user
}
If you want to update a user after, you can do it from the client that way :
Meteor.users.update({_id:Meteor.user()._id}, { $set: {what you want to update} });
By default, the users base will allow that (the current user may update itself). If you don't trust your users and want to ensure that everything is properly update, you can also forbid any updates from the client and make them via a Meteor.call() and proceed to the checkings server-side. But this would be sad.
Edit :
As said in the comments, adding options via the standard account-ui won't be possible. You'll only be able to update the user after the registration. To add options when you subscribe, you'll have to make you own form.
I won't insult you by writing html markup, but here is what you want to have after the submit event (and after the various checking) :
var options = {
username: $('input#username')[0].value,
emails: [{
address: $('input#email')[0].value,
verified: false
}],
password: $('input#password')[0].value,
profile: {
surname: $('input#surname')
},
};
Accounts.createUser( options , function(err){
if( err ) $('div#errors').html( err.message );
});
You only need the account-base package ; not the account-ui.
Login with the social networks is cake :
Meteor.loginWithFacebook({
requestPermissions: ['email', 'user_birthday', 'user_location']
}, function(error){loginCallBack(error);});
About the answer ram1 made :
This is not the way meteor works. You do not "POST" a form. You want all your client / server communication done via the websocket. The equivalent of what you are talking about is making a "Meteor.call('myserverfunction', myarguments, mycallback)" of a server method from the client and you pass the arguments you want the server to use.
But this is not the way you will get the best of meteor. There is the philosophy you want to work with :
you have datas in your local mini mongo you got from the server
you update locally those datas in your base / view
meteor do his magic to transmit those updates to the server
there the server can answer : ok, updates saved, this is seamless for you. Or answer : nop ! reverse the changes (and you can implement an error notification system)
(it can answer no because you don't have the permission to update this field, because this update break a rule you did set up...)
All you do is setting permissions and controls on the databases server-side. That way, when an honest client make an update, he sees the result instantly ; way before it has been pushed to the server and send to the other clients. This is latency compensation, one of the seven principles of meteor.
If you modify a data via Meteor.call, you will do that :
send an update to the server
the server checks and update the base
the server send the update to the clients (including you)
your local base updates and your view update => you see your update
=> this is what you had in yesterday app ; meteor allow you to build a today app. Don't apply the old recipes :)
The accepted answer has the HOW right, but the WHERE is outdated information. (Yes, this would be better as a comment on the answer, but I can't do that yet.)
From the Meteor 1.2 documentation:
The best way to store your custom data onto the Meteor.users collection is to add a new uniquely-named top-level field on the user document.
And regarding using Meteor.user.profile to store custom information:
🔗Don’t use profile
There’s a tempting existing field called profile that is added by
default when a new user registers. This field was historically
intended to be used as a scratch pad for user-specific data - maybe
their image avatar, name, intro text, etc. Because of this, the
profile field on every user is automatically writeable by that user
from the client. It’s also automatically published to the client for
that particular user.
Basically, it's probably fine to store basic information such as name, address, dob, etc in the profile field, but not a good idea to store anything beyond that as it will, by default, be writeable by the client and vulnerable to malicious users.
I had the same problem and managed to do it only with Accounts.createUser:
Accounts.createUser({
email: email,
password: password,
profile: {
givenName: 'John',
surname: 'Doe',
gender: 'M'
}
}
Thats very simple way and it works. Just add your desired variables in the profile section and it should be ready. Hope it helps someone.
I ended up using https://atmospherejs.com/joshowens/accounts-entry which offers an extraSignUpFields config option.
From the documentation (https://github.com/ianmartorell/meteor-accounts-ui-bootstrap-3/blob/master/README.md):
Custom signup options
You can define additional input fields to appear in the signup form, and you can decide wether to save these values to the profile object of the user document or not. Specify an array of fields using Accounts.ui.config like so:
Accounts.ui.config({
requestPermissions: {},
extraSignupFields: [{
fieldName: 'first-name',
fieldLabel: 'First name',
inputType: 'text',
visible: true,
validate: function(value, errorFunction) {
if (!value) {
errorFunction("Please write your first name");
return false;
} else {
return true;
}
}
}, {
fieldName: 'last-name',
fieldLabel: 'Last name',
inputType: 'text',
visible: true,
}, {
fieldName: 'gender',
showFieldLabel: false, // If true, fieldLabel will be shown before radio group
fieldLabel: 'Gender',
inputType: 'radio',
radioLayout: 'vertical', // It can be 'inline' or 'vertical'
data: [{ // Array of radio options, all properties are required
id: 1, // id suffix of the radio element
label: 'Male', // label for the radio element
value: 'm' // value of the radio element, this will be saved.
}, {
id: 2,
label: 'Female',
value: 'f',
checked: 'checked'
}],
visible: true
}, {
fieldName: 'country',
fieldLabel: 'Country',
inputType: 'select',
showFieldLabel: true,
empty: 'Please select your country of residence',
data: [{
id: 1,
label: 'United States',
value: 'us'
}, {
id: 2,
label: 'Spain',
value: 'es',
}],
visible: true
}, {
fieldName: 'terms',
fieldLabel: 'I accept the terms and conditions',
inputType: 'checkbox',
visible: true,
saveToProfile: false,
validate: function(value, errorFunction) {
if (value) {
return true;
} else {
errorFunction('You must accept the terms and conditions.');
return false;
}
}
}]
});
The official Meteor Guide provides a comprehensive answer with an example code:
The best way to store your custom data onto the Meteor.users collection is to add a new uniquely-named top-level field on the user document.
https://guide.meteor.com/accounts.html#custom-user-data

Resources