I am working to integrate the Stripe payment gateway in an Android app that I am developing. I have followed the steps listed on https://stripe.com/docs/mobile/android.
When I try to create a new card I get errors.
I run this in my activity
saveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Card card = new Card(
// Get values from the form
cardNumber.getText().toString(),
getInteger(monthSpinner),
getInteger(yearSpinner),
cvc.getText().toString()
);
}
});
Which uses the Card model.
Card model
package com.stripe.android.model;
import com.stripe.android.util.DateUtils;
import com.stripe.android.util.TextUtils;
public class Card extends com.stripe.model.StripeObject {
String number;
String cvc;
Integer expMonth;
Integer expYear;
String name;
String addressLine1;
String addressLine2;
String addressCity;
String addressState;
String addressZip;
String addressCountry;
String last4;
String type;
String fingerprint;
String country;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getCVC() {
return cvc;
}
public void setCVC(String cvc) {
this.cvc = cvc;
}
public Integer getExpMonth() {
return expMonth;
}
public void setExpMonth(Integer expMonth) {
this.expMonth = expMonth;
}
public Integer getExpYear() {
return expYear;
}
public void setExpYear(Integer expYear) {
this.expYear = expYear;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}
public String getAddressLine2() {
return addressLine2;
}
public void setAddressLine2(String addressLine2) {
this.addressLine2 = addressLine2;
}
public String getAddressCity() {
return addressCity;
}
public void setAddressCity(String addressCity) {
this.addressCity = addressCity;
}
public String getAddressZip() {
return addressZip;
}
public void setAddressZip(String addressZip) {
this.addressZip = addressZip;
}
public String getAddressState() {
return addressState;
}
public void setAddressState(String addressState) {
this.addressState = addressState;
}
public String getAddressCountry() {
return addressCountry;
}
public void setAddressCountry(String addressCountry) {
this.addressCountry = addressCountry;
}
public String getLast4() {
if (!TextUtils.isBlank(last4)) {
return last4;
}
if (number != null && number.length() > 4) {
return number.substring(number.length() - 4, number.length());
}
return null;
}
public String getType() {
if (TextUtils.isBlank(type) && !TextUtils.isBlank(number)) {
if (TextUtils.hasAnyPrefix(number, "34", "37")) {
return "American Express";
} else if (TextUtils.hasAnyPrefix(number, "60", "62", "64", "65")) {
return "Discover";
} else if (TextUtils.hasAnyPrefix(number, "35")) {
return "JCB";
} else if (TextUtils.hasAnyPrefix(number, "30", "36", "38", "39")) {
return "Diners Club";
} else if (TextUtils.hasAnyPrefix(number, "4")) {
return "Visa";
} else if (TextUtils.hasAnyPrefix(number, "5")) {
return "MasterCard";
} else {
return "Unknown";
}
}
return type;
}
public String getFingerprint() {
return fingerprint;
}
public String getCountry() {
return country;
}
public Card(String number, Integer expMonth, Integer expYear, String cvc, String name, String addressLine1, String addressLine2, String addressCity, String addressState, String addressZip, String addressCountry, String last4, String type, String fingerprint, String country) {
this.number = TextUtils.nullIfBlank(normalizeCardNumber(number));
this.expMonth = expMonth;
this.expYear = expYear;
this.cvc = TextUtils.nullIfBlank(cvc);
this.name = TextUtils.nullIfBlank(name);
this.addressLine1 = TextUtils.nullIfBlank(addressLine1);
this.addressLine2 = TextUtils.nullIfBlank(addressLine2);
this.addressCity = TextUtils.nullIfBlank(addressCity);
this.addressState = TextUtils.nullIfBlank(addressState);
this.addressZip = TextUtils.nullIfBlank(addressZip);
this.addressCountry = TextUtils.nullIfBlank(addressCountry);
this.last4 = TextUtils.nullIfBlank(last4);
this.type = TextUtils.nullIfBlank(type);
this.fingerprint = TextUtils.nullIfBlank(fingerprint);
this.country = TextUtils.nullIfBlank(country);
}
public Card(String number, Integer expMonth, Integer expYear, String cvc, String name, String addressLine1, String addressLine2, String addressCity, String addressState, String addressZip, String addressCountry) {
this(number, expMonth, expYear, cvc, name, addressLine1, addressLine2, addressCity, addressState, addressZip, addressCountry, null, null, null, null);
}
public Card(String number, Integer expMonth, Integer expYear, String cvc) {
this(number, expMonth, expYear, cvc, null, null, null, null, null, null, null, null, null, null, null);
this.type = getType();
}
public boolean validateCard() {
if (cvc == null) {
return validateNumber() && validateExpiryDate();
} else {
return validateNumber() && validateExpiryDate() && validateCVC();
}
}
public boolean validateNumber() {
if (TextUtils.isBlank(number)) {
return false;
}
String rawNumber = number.trim().replaceAll("\\s+|-", "");
if (TextUtils.isBlank(rawNumber)
|| !TextUtils.isWholePositiveNumber(rawNumber)
|| !isValidLuhnNumber(rawNumber)) {
return false;
}
if (!"American Express".equals(type) && rawNumber.length() != 16) {
return false;
}
if ("American Express".equals(type) && rawNumber.length() != 15) {
return false;
}
return true;
}
public boolean validateExpiryDate() {
if (!validateExpMonth()) {
return false;
}
if (!validateExpYear()) {
return false;
}
return !DateUtils.hasMonthPassed(expYear, expMonth);
}
public boolean validateExpMonth() {
if (expMonth == null) {
return false;
}
return (expMonth >= 1 && expMonth <= 12);
}
public boolean validateExpYear() {
if (expYear == null) {
return false;
}
return !DateUtils.hasYearPassed(expYear);
}
public boolean validateCVC() {
if (TextUtils.isBlank(cvc)) {
return false;
}
String cvcValue = cvc.trim();
boolean validLength = ((type == null && cvcValue.length() >= 3 && cvcValue.length() <= 4) ||
("American Express".equals(type) && cvcValue.length() == 4) ||
(!"American Express".equals(type) && cvcValue.length() == 3));
if (!TextUtils.isWholePositiveNumber(cvcValue) || !validLength) {
return false;
}
return true;
}
private boolean isValidLuhnNumber(String number) {
boolean isOdd = true;
int sum = 0;
for (int index = number.length() - 1; index >= 0; index--) {
char c = number.charAt(index);
if (!Character.isDigit(c)) {
return false;
}
int digitInteger = Integer.parseInt("" + c);
isOdd = !isOdd;
if (isOdd) {
digitInteger *= 2;
}
if (digitInteger > 9) {
digitInteger -= 9;
}
sum += digitInteger;
}
return sum % 10 == 0;
}
private String normalizeCardNumber(String number) {
if (number == null) {
return null;
}
return number.trim().replaceAll("\\s+|-", "");
}
}
This is the error I am getting.
This is new to me. What can I do to resolve this?
Since this is a top hit on Google search for 'How to integrate Stripe to Android Studio' and since Android studio removed the import module this is how I solved the import.
Right click on the project and select > New > Module
In your directories copy contents under Stripe > Stripe folder to the module folder (You should see a newly created folder. Delete the contents of this new folder and paste the contents of Stripe > Stripe)
Back to Android Studio navigate to build.gradle under src add compile project(":stripe") under dependencies.
Refresh your gradle.
EDIT 1
Since posting this answer some changes have happened. If you would wish to add stripe into your project do so via Maven. Just add this line to your app's build.gradle inside the dependencies section:
compile 'com.stripe:stripe-android:2.0.2'
EDIT 2 It's now implementation and not compile.
implementation 'com.stripe:stripe-android:6.1.2'
You can get more details here : https://stripe.com/docs/mobile/android
Okay, so I believe what's happening is that you don't have access to the proper Card() constructor because you haven't set things up properly.
Go to the github page and download the link [for the library]https://github.com/stripe/stripe-android. Unpack that folder and keep it handy.
Now, go into android studio and hit 'import module'. Navigate into that stripe-android directory that you just unzipped, and hit okay. Make sure you only have 'stripe' checked when importing, and not 'example' (only 'example' will be checked by default: fix this.)
Copy the jarfile stripe-java-1.12.0.jar to the directory :libs in your project (where you'd have other libraries). That jarfile should show up under the new 'stripe' directory in android studio.
Go into your src directory and find your app's build.gradle. You're going to want to add, under dependencies:
compile project(":stripe")
You may run into an error at some point saying that you need a newer version of build tools to build the project. If that's so, just start rummaging through the gradle files and changing numbers until it builds. That's what I do, at least.
Hope this helps!
(p.s: remember to include com.stripe.android.* and not com.stripe.*!)
Edit: Just ran into a new problem, and it turns out you should skip step 3. It'll cause dex to freak out that the same class is being defined in the same jarfile twice. So don't do it.
If you're not using Gradle then below is how I got it to work:
Download the zip from the stripe GitHub (stripe-android-master)
Import JUST the stripe folder as a module into your project. You shouldn't have to do anything fancy here.
It added to my project as "main". Go into Project Structure -> modules and add "main" as a module dependency to your working module
Click on the "main" (stripe) module and click the "Export" checkbox on the lib so that your working module has access to it
????
Profit
I just had exactly the same problem as the OP. I was importing some variant of Stripe code but did not have the multi-argument constructor or any of the specific methods I was looking for, so I clearly was not importing what I wanted/needed.
I tried many of the Import Module or Add Library incantations found here or elsewhere. Finally, about to give up, I tried that most desperate of all measures: RTFM. Or, in this case the README.md that came with the project I downloaded.
There, for Android Studio users, was the trivial solution that actually worked for me:
No need to clone the repository or download any files -- just add this line to your app's `build.gradle` inside the `dependencies` section:
compile 'com.stripe:stripe-android:+'
It worked like a charm.
Ironically, you don't have to download or clone to USE the library but so far the only way I know to get the README.md is to download the library files from here:
https://stripe.com/docs/mobile/android
Caveat: I wrote the above as soon as Android Studio started importing the right library and my IDE compilation errors went away. But as soon as I tried to actually build and run my code, I ran head on into the multiple dex horror for, I believe, pulling in multiple copies of the gson library that Stripe depends on and my code already uses. Sigh. I DID fix the problem by removing my local copy of the gson jar and, I presume, depending on the one located with the stripe package. Just deleting all the bin/intermediate/generated folders wasn't good enough. I'm not real happy with this solution but may live with it for now.
well, to use Stripe, you don't need to download anything from github or Stripe.com.
Here is how I do it.
Since I can't post image (with only 1 reputation), its destribed as below:
1. Right-click on your project
2. click Open Module Settings
3. Click dependencies
4. click add
5. click choose library dependencies
6. input "stripe"
7. click search
8. click com.stripe:stripe.android 1.0.0
9. click OK.
Card card = new Card(cardNumber.getText().toString(),getInteger(this.monthSpinner),getInteger(this.yearSpinner),cvc.getText().toString());
use this, the actual and formal parameter are not matching.
Thanks
For all those who could not find a fix with above answers.Here's what I did when I faced the same situation.
Make sure you have gradle dependency as: compile 'com.stripe:stripe-android:+'
In the class where you are using Card model-delete all the stripe import statements and then click on the Card and make sure to import from -
"com.stripe.android.model"
Hope that fixes the issue.Thanks
monthSpinner.getselecteditem() use this and pass it to the getinteger.
Same for the yearSpinner..
Related
I've created an Android Native Module in my React Native App to get User Installed Apps List. it works well on Debug but doesn't work on Release.
*Note : I've also tried to use a npm package called react-native-android-installed-apps, but it
doesn't work, so I decided to created a Native Module.
I don't know what's wrong that caused my Native Module works on Debug but doesn't work on Release. It will be great and I'll really appriciate it if anyone can point out to me what's wrong.
Here's my code:
#ReactMethod
public void getUserInstalledApps(Callback successCallback) throws JSONException {
PackageManager pm = reactContext.getPackageManager();
WritableArray array = new WritableNativeArray();
List<AppList> apps = new ArrayList<AppList>();
Gson g = new Gson();
List<PackageInfo> packs = reactContext.getPackageManager().getInstalledPackages(0);
for (int i = 0; i < packs.size(); i++) {
PackageInfo p = packs.get(i);
if ((!isSystemPackage(p))) {
String appName = p.applicationInfo.loadLabel(reactContext.getPackageManager()).toString();
String packages = p.applicationInfo.packageName;
apps.add(new AppList(appName, packages));
}
}
for (AppList co : apps) {
JSONObject jo = new JSONObject(g.toJson(co));
WritableMap wm = convertJsonToMap(jo);
array.pushMap(wm);
}
successCallback.invoke(array);
}
private boolean isSystemPackage(PackageInfo pkgInfo) {
return (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
public class AppList {
private String name;
private String packages;
public AppList(String name, String packages) {
this.name = name;
this.packages = packages;
}
public String getName() {
return name;
}
public String getPackages() {
return packages;
}
}
I've fix it by adding some pro-guard rules for my Native Module
I know that in Java a method can return only one return type... But if there is any possiblity to this, kindly let me know. From the below method I am trying to return a list if condition satisfies else i am trying to return an error message.
Here is my code:
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return "Incorrect loginId..Please enter valid loginId";
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return companyList;
} else {
return "You are not Authorized";
}
} else {
return "Incorrect Password";
}
Yes its possible, create a custom Exception say 'MyAppException' and throw that exception with the error message you want.
Write your logic in a try{}catch block and throw the exception in catch so that the response has the error message
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) throws MyAppppException
{
try
{
//your logic which throws error
return companyList;
}
catch( final MyAppException we )
{
throw new MyAppException("User not found", HttpStatus.NOT_FOUND);
}
}
Refer this link
https://www.codejava.net/java-core/exception/how-to-create-custom-exceptions-in-java
You can achieve this by creating a new presenter Class which contains List and status of type String and change the return type of getCompanies method to presenter class like
public CompaniesPresenter getCompanies()
And your CompaniesPresenter class should look like
public class CompaniesPresenter {
private List<CompanyMaster> companyMaster;
private string status;
//default constructor
public CompaniesPresenter(){
}
//parameterized constructor to return only string in exception case
public CompaniesPresenter(Stirng status){
this.status = status;
}
//parametirized constructor to return success case
public CompaniesPresenter(List<CompanyMaster> companyMaster, Stirng status){
this.companyMaster = companyMaster;
this.status = status;
}
//getters and setters
}
This is how your updated method lokks like
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public CompaniesPresenter getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return new CompaniesPresenter("Incorrect loginId..Please enter valid loginId");
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return new CompaniesPresenter(companyList,"success");
} else {
return new CompaniesPresenter("You are not Authorized");
}
} else {
return new CompaniesPresenter("Incorrect Password");
}
This is not tested please make sure for any compilation errors
vavr's Either class would be a good choice.
The usage of custom exception is most reasonable solution. However, creating custom exception for just one case is not ideal always.
Another solution is to return empty List from your method, check if the List is empty in your servlet (or wherever you are invoking this method from), and show error message there.
It seems like you want to return multiple error messages for different cases. In this case, custom exception is recommended solution. If you don't like custom exceptions, you can return List<Object> and populate error message as the first element in the list. In the place where this List is obtained, check if the first element is instanceOf String or CompanyMaster. Based on what it is, you can perform your operations. This is a weird but possible solution (only if you don't like custom exceptions).
You need to understand the problem first. You are mixing two things here, first authorization, does the user has correct privileges to get company details, second giving the company details itself. Let's understand the first problem when a user tries to access "/getcompanies" endpoint will you let him in if does not have access, in REST world your security model should take care of it. I would use spring security to achieve this. My recommendation would be to explore on "interceptor" and solve the problem of invalid user. This will make your other problem easy as your "/getcompanies" endpoint can focus only on getting the details and return it (SRP).
Need guideline -
How to do hard delete when no reference is available and do soft delete when reference is available, this operation should be performed in a single method itself.
E.g.
I have 1 master table and 3 transactional tables and the master reference is available in all 3 transactional tables.
Now while deleting master row - I have to do the following: If master reference is available then update the master table row and if no master ref. is available delete the row.
I tried following so far.
Service Implementation -
public response doHardOrSoftDelete(Employee emp) {
boolean flag = iMasterDao.isDataExist(emp);
if(flag) {
boolean result = iMasterDao.doSoftDelete(emp);
} else {
boolean result = iMasterDao.doHardDelete(emp);
}
}
Second Approach:
As we know that while deleting a record if the reference is available then it throws ConstraintViolationException so simply we can catch it and check that caught exception is of type ConstraintViolationException or not, if yes then call doSoftDelete() method and return the response. So here you don't need to write method or anything to check the references. But I'm not sure whether it is the right approach or not. Just help me with it.
Here is what I tried again -
public Response deleteEmployee(Employee emp) {
Response response = null;
try{
String status= iMasterDao.deleteEmployeeDetails(emp);
if(status.equals("SUCCESS")) {
response = new Response();
response.setStatus("Success");
response.setStatusCode("200");
response.setResult("True");
response.setReason("Record deleted successfully");
return response;
}else {
response = new Response();
response.setStatus("Fail");
response.setStatusCode("200");
response.setResult("False");
}
}catch(Exception e){
response = new Response();
Throwable t =e.getCause();
while ((t != null) && !(t instanceof ConstraintViolationException)) {
t = t.getCause();
}
if(t instanceof ConstraintViolationException){
boolean flag = iMasterDao.setEmployeeIsDeactive(emp);
if(flag) {
response.setStatus("Success");
response.setStatusCode("200");
response.setResult("True");
response.setReason("Record deleted successfully");
}else{
response.setStatus("Fail");
response.setStatusCode("200");
response.setResult("False");
}
}else {
response.setStatus("Fail");
response.setStatusCode("500");
response.setResult("False");
response.setReason("# EXCEPTION : " + e.getMessage());
}
}
return response;
}
Dao Implementation -
public boolean isDataExist(Employee emp) {
boolean flag = false;
List<Object[]> tbl1 = session.createQuery("FROM Table1 where emp_id=:id")
.setParameter("id",emp.getId())
.getResultList();
if(!tbl1.isEmpty() && tbl1.size() > 0) {
flag = true;
}
List<Object[]> tbl2 = session.createQuery("FROM Table2 where emp_id=:id")
.setParameter("id",emp.getId())
.getResultList();
if(!tbl2.isEmpty() && tbl2.size() > 0) {
flag = true;
}
List<Object[]> tbl3 = session.createQuery("FROM Table3 where emp_id=:id")
.setParameter("id",emp.getId())
.getResultList();
if(!tbl3.isEmpty() && tbl3.size() > 0) {
flag = true;
}
return flag;
}
public boolean doSoftDelete(Employee emp) {
empDet = session.get(Employee.class, emp.getId());
empDet .setIsActive("N");
session.update(empDet);
}
public boolean doHardDelete(Employee emp) {
empDet = session.get(Employee.class, emp.getId());
session.delete(empDet);
}
No matter how many transactional tables will be added with master tbl reference, my code should do the operations(soft/hard delete) accordingly.
In my case, every time new transactional tables get added with a master reference I've do the checks, so Simply I want to skip the isDataExist() method and do the deletions accordingly, how can I do it in a better way?
Please help me with the right approach to do the same.
There's a lot of repeated code in the body of isDataExist() method which is both hard to maintain and hard to extend (if you have to add 3 more tables the code will double in size).
On top of that the logic is not optimal as it will go over all tables even if the result from the first one is enough to return true.
Here is a simplified version (please note that I haven't tested the code and there could be errors, but it should be enough to explain the concept):
public boolean isDataExist(Employee emp) {
List<String> tableNames = List.of("Table1", "Table2", "Table3");
for (String tableName : tableNames) {
if (existsInTable(tableName, emp.getId())) {
return true;
}
}
return false;
}
private boolean existsInTable(String tableName, Long employeeId) {
String query = String.format("SELECT count(*) FROM %s WHERE emp_id=:id", tableName);
long count = (long)session
.createQuery(query)
.setParameter("id", employeeId)
.getSingleResult();
return count > 0;
}
isDataExist() contains a list of all table names and iterates over these until the first successful encounter of the required Employee id in which case it returns true. If not found in any table the method returns false.
private boolean existsInTable(String tableName, Long employeeId) is a helper method that does the actual search for employeeId in the specified tableName.
I changed the query to just return the count (0 or more) instead of a the actual entity objects as these are not required and there's no point to fetch them.
EDIT in response to the "Second approach"
Is the Second Approach meeting the requirements?
If so, then it is a "right approach" to the problem. :)
I would refactor the deleteEmployeeDetails method to either return a boolean (if just two possible outcomes are expected) or to return a custom Enum as using a String here doesn't seem appropriate.
There is repeated code in deleteEmployeeDetails and this is never a good thing. You should separate the logic which decides the type of the response from the code that builds it, thus making your code easier to follow, debug and extend when required.
Let me know if you need a code example of the ideas above.
EDIT #2
Here is the sample code as requested.
First we define a Status enum which should be used as return type from MasterDao's methods:
public enum Status {
DELETE_SUCCESS("Success", "200", "True", "Record deleted successfully"),
DELETE_FAIL("Fail", "200", "False", ""),
DEACTIVATE_SUCCESS("Success", "200", "True", "Record deactivated successfully"),
DEACTIVATE_FAIL("Fail", "200", "False", ""),
ERROR("Fail", "500", "False", "");
private String status;
private String statusCode;
private String result;
private String reason;
Status(String status, String statusCode, String result, String reason) {
this.status = status;
this.statusCode = statusCode;
this.result = result;
this.reason = reason;
}
// Getters
}
MasterDao methods changed to return Status instead of String or boolean:
public Status deleteEmployeeDetails(Employee employee) {
return Status.DELETE_SUCCESS; // or Status.DELETE_FAIL
}
public Status deactivateEmployee(Employee employee) {
return Status.DEACTIVATE_SUCCESS; // or Status.DEACTIVATE_FAIL
}
Here is the new deleteEmployee() method:
public Response deleteEmployee(Employee employee) {
Status status;
String reason = null;
try {
status = masterDao.deleteEmployeeDetails(employee);
} catch (Exception e) {
if (isConstraintViolationException(e)) {
status = masterDao.deactivateEmployee(employee);
} else {
status = Status.ERROR;
reason = "# EXCEPTION : " + e.getMessage();
}
}
return buildResponse(status, reason);
}
It uses two simple utility methods (you can make these static or export to utility class as they do not depend on the internal state).
First checks if the root cause of the thrown exception is ConstraintViolationException:
private boolean isConstraintViolationException(Throwable throwable) {
Throwable root = throwable;
while (root != null && !(root instanceof ConstraintViolationException)) {
root = root.getCause();
}
return root != null;
}
And the second one builds the Response out of the Status and a reason:
private Response buildResponse(Status status, String reason) {
Response response = new Response();
response.setStatus(status.getStatus());
response.setStatusCode(status.getStatusCode());
response.setResult(status.getResult());
if (reason != null) {
response.setReason(reason);
} else {
response.setReason(status.getReason());
}
return response;
}
If you do not like to have the Status enum loaded with default Response messages, you could strip it from the extra info:
public enum Status {
DELETE_SUCCESS, DELETE_FAIL, DEACTIVATE_SUCCESS, DEACTIVATE_FAIL, ERROR;
}
And use switch or if-else statements in buildResponse(Status status, String reason) method to build the response based on the Status type.
I'm attempting to convert the JSON output from my session and map it to a class that I've created using JAVA's ObjectMapper. When I run my tests on Lambda I get a Deserialisation error:
Deserialization error: com.amazon.ask.exception.AskSdkException
com.amazon.ask.exception.AskSdkException: Deserialization error
at com.amazon.ask.util.impl.JacksonJsonUnmarshaller.unmarshall(JacksonJsonUnmarshaller.java:50)
at com.amazon.ask.impl.AbstractSkill.execute(AbstractSkill.java:44)
at com.amazon.ask.AlexaSkill.execute(AlexaSkill.java:22)
at com.amazon.ask.SkillStreamHandler.handleRequest(SkillStreamHandler.java:71)
Caused by: com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'AnswerIntent' as a subtype of [simple type, class com.amazon.ask.model.Request]: known type ids = [Alexa.Presentation.APL.UserEvent, AlexaHouseholdListEvent.ItemsCreated, AlexaHouseholdListEvent.ItemsDeleted, AlexaHouseholdListEvent.ItemsUpdated, AlexaHouseholdListEvent.ListCreated, AlexaHouseholdListEvent.ListDeleted, AlexaHouseholdListEvent.ListUpdated, AlexaSkillEvent.SkillAccountLinked, AlexaSkillEvent.SkillDisabled, AlexaSkillEvent.SkillEnabled, AlexaSkillEvent.SkillPermissionAccepted, AlexaSkillEvent.SkillPermissionChanged, AudioPlayer.PlaybackFailed, AudioPlayer.PlaybackFinished, AudioPlayer.PlaybackNearlyFinished, AudioPlayer.PlaybackStarted, AudioPlayer.PlaybackStopped, Connections.Request, Connections.Response, Display.ElementSelected, GameEngine.InputHandlerEvent, IntentRequest, LaunchRequest, Messaging.MessageReceived, PlaybackController.NextCommandIssued, PlaybackController.PauseCommandIssued, PlaybackController.PlayCommandIssued, PlaybackController.PreviousCommandIssued, SessionEndedRequest, System.ExceptionEncountered] (for POJO property 'request')
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.amazon.ask.model.RequestEnvelope$Builder["request"])
at com.fasterxml.jackson.databind.exc.InvalidTypeIdException.from(InvalidTypeIdException.java:43)
at com.fasterxml.jackson.databind.DeserializationContext.invalidTypeIdException(DeserializationContext.java:1628)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownTypeId(DeserializationContext.java:1186)
at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._handleUnknownTypeId(TypeDeserializerBase.java:291)
at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:162)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:113)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:97)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:254)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:151)
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.vanillaDeserialize(BuilderBasedDeserializer.java:269)
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserialize(BuilderBasedDeserializer.java:193)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3972)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2264)
at com.fasterxml.jackson.databind.ObjectMapper.treeToValue(ObjectMapper.java:2746)
at com.amazon.ask.util.impl.JacksonJsonUnmarshaller.unmarshall(JacksonJsonUnmarshaller.java:48)
... 3 more
I did checks to ensure that my "riddleItem" variable is not null. The JSON values are being mapped to the Person class which just returns properties of a person. The code is shown below and I've highlighted the line which the error occurs on:
public Optional<Response> handle(HandlerInput input) {
Map<String, Object> sessionAttributes = input.getAttributesManager().getSessionAttributes();
System.out.println("This a FIRST debug");
LOG.debug("This a FIRST debug");
boolean correctAnswer;
String speechText = null, response;
System.out.println("This a SECOND debug");
Map<String, String> riddleItem = (LinkedHashMap<String, String>)sessionAttributes.get(Attributes.RIDDLE_ITEM_KEY);
Person person;
// System.out.println("riddleItem " + riddleItem);
if(riddleItem != null)
{
person = MAPPER.convertValue(riddleItem, Person.class); // ERROR OCCURS ON THIS LINE
}
System.out.println("This a THIRD debug");
PersonProperty personProperty = PersonProperty.valueOf((String) sessionAttributes.get(Attributes.RIDDLE_PROPERTY_KEY));
int counter = (int) sessionAttributes.get(Attributes.COUNTER_KEY);
int riddleGameScore = (int) sessionAttributes.get(Attributes.RIDDLE_SCORE_KEY);
System.out.println("This a FOURTH debug");
IntentRequest intentRequest = (IntentRequest) input.getRequestEnvelope().getRequest();
correctAnswer = compareSlots(intentRequest.getIntent().getSlots(), getPropertyOfPerson(personProperty, person));
System.out.println("This a FIFTH debug " + correctAnswer);
if(correctAnswer)
{
riddleGameScore++;
response = getSpeechExpressionCon(true);
System.out.println("This a SIXTH debug " + response);
sessionAttributes.put(Attributes.RIDDLE_SCORE_KEY, riddleGameScore);
}
else
{
response = getSpeechExpressionCon(false);
System.out.println("This a SEVENTH debug " + response);
}
AnswerIntentHandler setup = new AnswerIntentHandler();
//
if(riddle.getAnswer() != null)
{
speechText = "Hello " + riddle.getAnswer();
}
return input.getResponseBuilder()
.withSimpleCard("RiddleSession", speechText)
.withSpeech(speechText)
.withShouldEndSession(true)
.build();
}
[Json Output of properties under "riddleItem" during Session]1
I know my the values being mapped aren't empty thus I'm at a complete loss of ideas as to what's going on as I've come up short with possible ideas as to what the problem might be.
I solved the problem as I came to realise that when mapping from JSON to a class, methods ('set' methods) for assigning the JSON values to the variables in the class must be created. A sample structure for example:
public class State {
public State() {}
public State(String name, String abbreviation, String capital, String statehoodYear, String statehoodOrder) {
this.name = name;
this.abbreviation = abbreviation;
this.capital = capital;
this.statehoodYear = statehoodYear;
this.statehoodOrder = statehoodOrder;
}
public String getName() {
return name;
}
public String getAbbreviation() {
return abbreviation;
}
public String getCapital() {
return capital;
}
public String getStatehoodYear() { return statehoodYear; }
public String getStatehoodOrder() {
return statehoodOrder;
}
public void setName(String name) {
this.name = name;
}
public void setAbbreviation(String abbreviation) {
this.abbreviation = abbreviation;
}
public void setCapital(String capital) {
this.capital = capital;
}
public void setStatehoodYear(String statehoodYear) {
this.statehoodYear = statehoodYear;
}
public void setStatehoodOrder(String statehoodOrder) {
this.statehoodOrder = statehoodOrder;
}
}
The declaration of an empty constructor is necessary when making use of multiple constructors where, one is parametric. In some cases without the inclusion of such constructor an error may be thrown so, to avoid the possibility of said error, adding the constructor as a "Dummy" so to say, is essential.
Good evening, I have a problem with my web application in java app, I would like to create a new object "appuntamento", that object is also linked to another object "people." My idea is to initially choose a value for the "people" and then bring it back in the form editAppuntamento.
Here is the code of the controller object "appointment"
//show list people
public static Result scegliCliente(int page, String sortBy, String order, String filter, String filterNome, String filterTel, Long idApp) {
return ok(
scegliCliente.render(People.pagePeople(page, 25, sortBy, order, filter, filterNome, filterTel), sortBy, order, filter, filterNome, filterTel, idApp));
}
//redirect to method editappuntamento
public static Result sceltaCliente(Long idCliente, Long idApp) {
models.Appuntamento app = models.Appuntamento.find.byId(idApp);
People cliente = People.find.byId(idCliente);
app.cliente = cliente;
return redirect(
controllers.appuntamenti.routes.GestioneApp.editAppuntamento(idApp, idCliente)
);
}
//method edit appuntamento
public static Result editAppuntamento(Long idApp, Long idCliente) {
Form<Appuntamento> thisForm = Form.form(Appuntamento.class).fill(Appuntamento.find.byId(idApp));
return ok(
editAppuntamento.render(thisForm, idApp, idCliente)
);
}
//save object
public static Result saveAppuntamento(Long idAppuntamento, Long idCliente) {
Form<Appuntamento> thisForm = Form.form(Appuntamento.class).bindFromRequest();
if(thisForm.hasErrors()) {
Logger.debug("form: "+thisForm);
flash("error","Tipo e Data obbligatori.");
return badRequest(editAppuntamento.render(thisForm, idAppuntamento, idCliente));
}
People people = People.find.byId(idCliente);
Appuntamento appuntamento = thisForm.get();
appuntamento.cliente = people;
if(idAppuntamento == 0){
appuntamento.save();
}else{
appuntamento.update();
}
return redirect( controllers.appuntamenti.routes.GestioneApp.list(0,"","","","",""));
}
what is wrong?
thanks so much