Google cloud pubsub subscription not found on cloud but found on local - spring-boot

so I have weird problem, I have subscription in Google Cloud pub sub. In my local machine, the program can find the subscription without any problem, but in cloud (I use GCP VM) I get this error:
com.google.api.gax.rpc.NotFoundException: com.google.api.gax.rpc.NotFoundException: io.grpc.StatusRuntimeException: NOT_FOUND: Resource not found (resource=subs_id).
Eventhough I used subs_id in my local too.
My setup is like this:
Service account credentials.json:
{
"type": "service_account",
"project_id": "project-id-123",
"private_key_id": "privateKeyId",
"private_key": "-----BEGIN PRIVATE KEY-----\nprivateKey\n-----END PRIVATE KEY-----\n",
"client_email": "user#project-id-123.iam.gserviceaccount.com",
"client_id": "clientId",
"auth_uri": "auth_uri",
"token_uri": "token_uri",
"auth_provider_x509_cert_url": "certs_url",
"client_x509_cert_url": "clients_certs_url
}
Code (mostly copied from their documentation):
#Component public class GoogleCloudPubSubListenerImpl implements
GoogleCloudPubSubListener, InitializingBean {
private static final String PROJECT_ID =
ServiceOptions.getDefaultProjectId();
private static final Logger LOGGER =
LoggerFactory.getLogger(GoogleCloudPubSubListenerImpl.class);
#Value("${pub.sub.subscription}")
private String subId;
#Override public void listenPubSub() {
ProjectSubscriptionName subscriptionName = ProjectSubscriptionName.of(PROJECT_ID, subId);
Subscriber subscriber;
try {
subscriber = Subscriber
.newBuilder(subscriptionName,
new someService()).build();
subscriber.startAsync().awaitRunning();
} catch (Exception e) {
LOGGER.error("Subscriber err : {}, {}", e.getLocalizedMessage(), e);
} }
In cloud I run this command:
gcloud pubsub subscriptions pull subs_id
ERROR: (gcloud.pubsub.subscriptions.pull) NOT_FOUND: Resource not found (resource=subs_id).
But this command succeed:
gcloud pubsub subscriptions pull projects/project-id-123/subscriptions/subs_id
Listed 0 items.
When I change the subs_id in my program to projects/project-id-123/subscriptions/subs_id
I get error:
Invalid character "/" in path section "projects/project-id-123/subscriptions/subs_id".
Anyone having same problem? Need advice here ... Thank you

Related

Push Notification with FCM + Spring Boot, SenderId mismatch error

I'm doing a project using Spring Boot to send notifications to my React Native app. Since I'm using Expo to my React Native app, I did a different configuration following this tutorial and tested with Notifications composer from Firebase and worked well.
For the Spring Boot, I used this another tutorial to configure FCM, also I create a Private Key file from SDK admin in Firebase to use here:
try {
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(new ClassPathResource(MY_PRIVATE_KEY_FILE).getInputStream()))
.build();
if (FirebaseApp.getApps().isEmpty()) {
FirebaseApp.initializeApp(options);
}
} catch (IOException e) {
e.printStackTrace();
}
So I did a method to send the message using token from Server Key like this:
private String token="AAAAm_p0hoc:************************************";
public void sendPushNotificationWithData() {
PushNotificationRequest pushNotificationRequest = new PushNotificationRequest();
pushNotificationRequest.setMessage("Send push notifications from Spring Boot server");
pushNotificationRequest.setTitle("test Push Notification");
pushNotificationRequest.setToken(token);
Map<String, String> appData= new HashMap<>();
appData.put("name", "PushNotification");
try {
fcmService.sendMessage(appData, pushNotificationRequest);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
But I'm just having this response from FCM:
POST https://fcm.googleapis.com/v1/projects/myproject/messages:send
{
"error": {
"code": 403,
"message": "SenderId mismatch",
"status": "PERMISSION_DENIED",
"details": [
{
"#type": "type.googleapis.com/google.firebase.fcm.v1.FcmError",
"errorCode": "SENDER_ID_MISMATCH"
}
]
}
}
I tried to make a new project into FireBase, update the private and server keys but still having the same response.
Is something I forgot to set up or am I doing it wrong?

Spring: Can't map YAML to list of objects

I have a yaml file with errors and error messages. I'm attempting to load them into a class, RefreshErrors within a common jar used by my Spring Boot microservices. The common jar does have Spring Boot on the classpath, but there is no SpringBootApplication - it's simply included by other applications. So that may be why I'm not getting any values in RefreshErrors?
Here's the yaml, which is in application.yml, located under src/main/resources:
refreshErrorsProperties:
refreshErrors:
-
recordStatus: "Error"
errorCode: "502 Bad Gateway"
errorMessage: "AWS service is tempoarily not available. ESS has been notified. Please try refreshing again after the service becomes available."
-
recordStatus: "Error"
errorCode: "503 Service Temporarily Unavailable"
errorMessage: "AWS service is tempoarily not available. ESS has been notified. Please try refreshing again after the service becomes available."
-
...
And here's my configuration for the class I want the yaml mapped to:
#Data
#Component
#ConfigurationProperties(prefix="refreshErrors")
public class RefreshErrorsProperties {
private List<RefreshErrors> refreshErrors = new ArrayList<>();
}
The RefreshErrors class:
#Data
#AllArgsConstructor
public class RefreshErrors {
private String errorCode;
private String recordStatus;
private String errorMessage;
}
I autowire RefreshErrors in another class (see below) and I do get an object reference, but all the values inside (like errorCode etc) are empty. Any help would be appreciated!
#Autowired
public void setRefreshErrorsProperties(RefreshErrorsProperties refreshErrorsProperties) {
RefreshJobDetailHelper.refreshErrorsProperties = refreshErrorsProperties;
}
...
RefreshErrors error;
if (exception != null) {
String fullException = ExceptionUtils.getStackTrace(exception);
error = refreshErrorsProperties.getRefreshErrors().stream()
.filter(f -> fullException.contains(f.getErrorCode()))
.findAny()
.orElseGet((Supplier<? extends RefreshErrors>) new RefreshErrors("Error", ERROR.getValue(), "An error has occurred while refreshing this record. ESS has been notified. Please try refreshing again after the issue has been resolved."));
} else {
error = new RefreshErrors("No data", ERROR_NO_DATA.getValue(), "Data is not available in the source for this product, domain, and effective date.");
}
The issue here is with the prefix #ConfigurationProperties(prefix = "refreshErrors"). It should be like #ConfigurationProperties, with no prefix, the prefix is required when you need to map properties under the prefix, but refreshErrors is the property you want to map and not the prefix. The below code works fine.
I have refactored a bit :
properties file:
refresh-errors:
-
record-status: "Error"
error-code: "502 Bad Gateway"
error-message: "AWS service is tempoarily not available. ESS has been notified. Please try refreshing again after the service becomes available."
-
record-status: "Error"
error-code: "503 Service Temporarily Unavailable"
error-message: "AWS service is tempoarily not available. ESS has been notified. Please try refreshing again after the service becomes available."
PropertiesMapperClass :
#Data
#Component
#ConfigurationProperties
public class RefreshErrorsProperties {
private List<RefreshErrors> refreshErrors = new ArrayList<>();
#Data
public static class RefreshErrors {
private String errorCode;
private String recordStatus;
private String errorMessage;
}
}
if you don't change your annotation,the yaml file node refreshErrors's attribute should not be a list,
the yaml file should be
refresh-errors:
record-status: "Error"
error-code: "502 Bad Gateway"
error-message: "AWS service ..."
which may not be what you want,
if you don't change your yaml file,
the code should be like Mohit Singh shows

Properties of custom Spring Cloud Service Connector not published to cloud.services.*

I created a custom Spring Cloud Service Connector by defining the following two classes:
#ServiceLabel("kafka")
public class KafkaServiceInfo extends BaseServiceInfo {
private static Logger logger = Logger.getLogger(KafkaServiceInfo.class.getName());
public static final String BROKERS = "brokers";
public static final String REGISTRY = "schemaregistry";
protected List<String> brokers;
protected String registry;
public KafkaServiceInfo(String id, List<String> brokers, String registry) {
super(id);
this.brokers = brokers;
this.registry = registry;
}
#ServiceProperty
public String getRegistry() {
return registry;
}
#ServiceProperty
public List<String> getBrokers() {
return brokers;
}
}
And this class:
public class KafkaServiceInfoCreator extends CloudFoundryServiceInfoCreator<KafkaServiceInfo> {
private static Logger logger = Logger.getLogger(KafkaServiceInfoCreator.class.getName());
public static final String USER_PROVIDED_SERVICE_NAME = "kafka";
public KafkaServiceInfoCreator() {
super(new Tags(USER_PROVIDED_SERVICE_NAME), null);
}
public KafkaServiceInfo createServiceInfo(Map<String, Object> serviceData) {
String id = getId(serviceData);
Map<String, Object> credentials = getCredentials(serviceData);
List<String> brokers = (List<String>) credentials.get(KafkaServiceInfo.BROKERS);
String registry = (String) credentials.get(KafkaServiceInfo.REGISTRY);
logger.info("KafkaServiceInfo created for Cloud Foundry Service \"" + id + "\"");
logger.info("Kafka Brokers configured for Cloud Foundry Service: " + brokers.toString());
logger.info("Schema Registry configured for Cloud Foundry Service: " + registry);
return new KafkaServiceInfo(id, brokers, registry);
}
#Override
public boolean accept(Map<String, Object> serviceData) {
return getId(serviceData).contains(USER_PROVIDED_SERVICE_NAME);
}
}
On my PCF instance, I created a user-provided service that looks in the VCAPS env variables like this:
"user-provided": [
{
"credentials": {
"brokers": [
"<some-ip-here>:29092"
],
"schemaregistry": "http://<some-ip-here>:8081"
},
"label": "user-provided",
"name": "kafka",
"syslog_drain_url": "",
"tags": [],
"volume_mounts": []
}
]
I also added the service definition file into the META-INF folder.
src/main/resources/META-INF/services/org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator
with the content:
path.to.my.file.KafkaServiceInfoCreator
I would now expect to see the properties whose getters are annotated with #ServiceProperty in the cloud.services.kafka.* path. However, they do not show up. Instead, I just have the following 2 entries:
"cloud.services.kafka.id": "kafka",
"cloud.services.null.id": "kafka",
I am wondering what is going wrong here and also why I have the second entry with the "null" in between.
Any ideas what could be wrong here? The classes somehow seem to be found since I get the log messages defined in the creator class above.
Regards, Lars
The #ServiceProperty annotation expects to be provided with either a category or name attribute. These values are used to build the key that is placed into the cloud.services map. If neither category or name attribute are provided, the #ServiceProperty annotation does not result in the property appearing in the map.
A typical usage is #ServiceProperty(category="connection"), with the name defaulting to the name of the property using bean naming rules. In your case, adding the category="connection" attribute should result in
"cloud.services.kafka.id": "kafka",
"cloud.services.kafka.connection.registry": "http://<some-ip-here>:8081",
"cloud.services.kafka.connection.brokers": ["<some-ip-here>:29092"],
I'm not sure yet where the "cloud.services.null.id": "kafka" entry is coming from. Let me know if that still appears after adding the attributes to the #ServiceProperty annotation.
After digging quite a bit deeper, I found the "reason" explained here.
The cloud foundry java buildpack includes the auto-reconfiguration library which by itself contains a copy of the org.springframework.cloud namespace. This copy does NOT consider any custom ServiceInfo classes.
An since it is also this auto-reconfiguration that exposes the cloud.services.* properties to the environment, my personal ones are not picked-up and exposed as well. So I will switch this auto-reconfiguration off and configure the required stuff manually.
The spring cloud connector documentation is misleading here as well, since the properties in cloud.service.* are only added by java auto reconfiguration to the environment.

Feign with RibbonClient and Consul discovery without Spring Cloud

I was trying to setup Feign to work with RibbonClient, something like MyService api = Feign.builder().client(RibbonClient.create()).target(MyService.class, "https://myAppProd");, where myAppProd is an application which I can see in Consul. Now, if I use Spring annotations for the Feign client (#FeignClient("myAppProd"), #RequestMapping), everything works as Spring Cloud module will take care of everything.
If I want to use Feign.builder() and #RequestLine, I get the error:
com.netflix.client.ClientException: Load balancer does not have available server for client: myAppProd.
My first initial thought was that Feign was built to work with Eureka and only Spring Cloud makes the integration with Consul, but I am unsure about this.
So, is there a way to make Feign work with Consul without Spring Cloud?
Thanks in advance.
In my opinion, it's not feign work with consul, its feign -> ribbon -> consul.
RibbonClient needs to find myAppProd's serverList from its LoadBalancer.
Without ServerList, error: 'does not have available server for client'.
This job has been done by SpringCloudConsul and SpringCloudRibbon project, of course you can write another adaptor, it's just some glue code. IMHO, you can import this spring dependency into your project, but use it in non-spring way . Demo code:
just write a new feign.ribbon.LBClientFactory, that generate LBClient with ConsulServerList(Spring's class).
public class ConsulLBFactory implements LBClientFactory {
private ConsulClient client;
private ConsulDiscoveryProperties properties;
public ConsulLBFactory(ConsulClient client, ConsulDiscoveryProperties consulDiscoveryProperties) {
this.client = client;
this.properties = consulDiscoveryProperties;
}
#Override
public LBClient create(String clientName) {
IClientConfig config =
ClientFactory.getNamedConfig(clientName, DisableAutoRetriesByDefaultClientConfig.class);
ConsulServerList consulServerList = new ConsulServerList(this.client, properties);
consulServerList.initWithNiwsConfig(config);
ZoneAwareLoadBalancer<ConsulServer> lb = new ZoneAwareLoadBalancer<>(config);
lb.setServersList(consulServerList.getInitialListOfServers());
lb.setServerListImpl(consulServerList);
return LBClient.create(lb, config);
}
}
and then use it in feign:
public class Demo {
public static void main(String[] args) {
ConsulLBFactory consulLBFactory = new ConsulLBFactory(
new ConsulClient(),
new ConsulDiscoveryProperties(new InetUtils(new InetUtilsProperties()))
);
RibbonClient ribbonClient = RibbonClient.builder()
.lbClientFactory(consulLBFactory)
.build();
GitHub github = Feign.builder()
.client(ribbonClient)
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
List<Contributor> contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
interface GitHub {
#RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(#Param("owner") String owner, #Param("repo") String repo);
}
public static class Contributor {
String login;
int contributions;
}
}
you can find this demo code here, add api.github.com to your local consul before running this demo.

Is it possible to create a custom Alexa skill with Spring Boot https endpoint

I am trying to create a Custom Alexa Skill without using Lambda. As such, I have deployed a Spring Boot application to an AWS EC2 instance, setup an SSL certificate, and tested that the service is functional by using Postman to invoke it.
I then setup an Alexa skill as an "https" endpoint. When I using the Test form on developer.amazon.com, I simply get back:
The remote endpoint could not be called, or the response it returned was invalid.
When I invoke the service directly with Postman, I get:
{
"version": "1.0",
"response": {
"outputSpeech": {
"type": "PlainText",
"id": null,
"text": "Hello, World. I am a Spring Boot custom skill."
},
"card": {
"type": "Simple",
"title": "HelloWorld",
"content": "Hello, World. I am a Spring Boot custom skill."
},
"reprompt": null,
"shouldEndSession": true
},
"sessionAttributes": null
}
My controller uses the Alexa Skill Set SDK. Here's the code:
#RestController
public class AlexaController {
#RequestMapping(value="/alexa",
method=RequestMethod.POST,
produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SpeechletResponseEnvelope> alexa(Model model) {
String speechText = "Hello, World. I am a Spring Boot custom skill.";
SimpleCard card = new SimpleCard();
card.setTitle("HelloWorld");
card.setContent(speechText);
PlainTextOutputSpeech speech = new PlainTextOutputSpeech();
speech.setText(speechText);
SpeechletResponse response = SpeechletResponse.newTellResponse(speech, card);
SpeechletResponseEnvelope envelope = new SpeechletResponseEnvelope();
envelope.setResponse(response);
envelope.setVersion("1.0");
envelope.setSessionAttributes(null);
return new ResponseEntity<SpeechletResponseEnvelope>(envelope, HttpStatus.OK);
}
}
So, I scrapped the above and instead registered a custom servlet using Spring's ServletRegistrationBean class.
#Configuration
public class AlexaConfig {
#Autowired
private MyCustomSpeechlet mySpeechlet;
#Bean
public ServletRegistrationBean registerServlet() {
SpeechletServlet speechletServlet = new SpeechletServlet();
speechletServlet.setSpeechlet(mySpeechlet);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(speechletServlet, "/alexa");
return servletRegistrationBean;
}
}
My custom Servlet extends The Alexa Skill Kit class Speechlet.
#Service
public class MyCustomSpeechlet implements Speechlet {
#Override
public void onSessionStarted(SessionStartedRequest request, Session session) throws SpeechletException {
}
#Override
public SpeechletResponse onLaunch(LaunchRequest request, Session session) throws SpeechletException {
}
#Override
public SpeechletResponse onIntent(IntentRequest request, Session session) throws SpeechletException {
Intent intent = request.getIntent();
if (intent == null)
throw new SpeechletException("Unrecognized intent");
String intentName = intent.getName();
if ( intentName.equals("TerriblyInterestingIntent") ) {
String speechText = "Hello, World. I am a Spring Boot custom skill.";
SimpleCard card = new SimpleCard();
card.setTitle("Hello World");
card.setContent(speechText);
PlainTextOutputSpeech speech = new PlainTextOutputSpeech();
speech.setText(speechText);
SpeechletResponse response = SpeechletResponse.newTellResponse(speech, card);
return response;
}
else {
throw new SpeechletException("I don't understand that intent.");
}
}
#Override
public void onSessionEnded(SessionEndedRequest request, Session session) throws SpeechletException {
}
}
Works like a charm!

Resources