Firestore Add Custom Objects with Reference Attribute - java

I have been adding POJOs to Firestore that automatically interprets them as JSON objects for the database. However I want to have one of my POJOs have what Firestore calls a reference type. Would the attribute type just be DocumentReference instead of a String?
I'm working on an Android project using Java.
Here is the custom object example from the Firebase Docs.
public class City {
private String name;
private String state;
private String country;
private boolean capital;
private long population;
private List<String> regions;
public City() {}
public City(String name, String state, String country, boolean capital, long population, List<String> regions) {
// ...
}
public String getName() {
return name;
}
public String getState() {
return state;
}
public String getCountry() {
return country;
}
public boolean isCapital() {
return capital;
}
public long getPopulation() {
return population;
}
public List<String> getRegions() {
return regions;
}
}
Then to add to the database
City city = new City("Los Angeles", "CA", "USA",
false, 5000000L, Arrays.asList("west_coast", "sorcal"));
db.collection("cities").document("LA").set(city);

I've done some simple testing and figured it out.
The attribute type is indeed DocumentReference for custom objects when adding directly to Firestore.
Here is an example where the creator of a Group is a reference to a user in the database:
//Class POJO that holds data
public class Group {
private String name;
private DocumentReference creator;
public Group(){}
public Group(String name, DocumentReference ref) {
this.name = name;
this.creator = ref;
}
public String getName() { return this.name; }
public DocumentReference getCreator() { return this.creator; }
}
// Add to database
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DocumentReference ref = db.collection("users").document(uid);
Group newGroup = new Group("My Group", ref);
db.collection("groups").document().set(newGroup);

Related

How to test builder with Mockito - Java

I have a builder class written in Java that I would like to test with Mockito.
Profile.java
#Data
#Document
public class Profile {
public final String birthDate;
public final City city;
public final Country country;
public final String imageId;
public final Team team;
public Profile(String birthDate,
City city,
Country country,
String imageId,
Team team) {
this.birthDate = birthDate;
this.city = city;
this.country = country;
this.imageId = imageId;
this.team = team;
}
public static ProfileBuilder builder() {
return new ProfileBuilder();
}
public static final class ProfileBuilder {
public String birthDate;
public City city;
public Country country;
public String imageId;
public Team team;
public ProfileBuilder() {
}
public ProfileBuilder withBirthDate(String birthDate) {
this.birthDate = birthDate;
return this;
}
public ProfileBuilder withCity(City city) {
this.city = city;
return this;
}
public ProfileBuilder withCountry(Country country) {
this.country = country;
return this;
}
public ProfileBuilder withImageId(String imageId) {
this.imageId = imageId;
return this;
}
public ProfileBuilder withTeam(Team team) {
this.team = team;
return this;
}
public Profile build(){
return new Profile(birthDate, city, country, imageId, team);
}
}
}
And I have this method to add Profile to database
#Override
public Profile addProfile(Profile profile) {
Profile createdProfile = Profile.builder()
.withBirthDate(profile.getBirthDate())
.withCity(profile.getCity())
.withCountry(profile.getCountry())
.withTeam(profile.getTeam())
.withImageId(profile.getImageId())
.build();
return profileRepository.save(createdProfile);
}
I am trying to test it like this:
public class ProfileServiceImplTest {
ProfileRepository profileRepository = Mockito.mock(ProfileRepository.class);
private final ProfileServiceImpl profileService = new ProfileServiceImpl(profileRepository);
City city = Mockito.mock(City.class);
Country country = Mockito.mock(Country.class);
Team team = Mockito.mock(Team.class);
#Test
public void addProfileTest(){
Profile profile = new Profile("25.07.1996", city, country, "imageId", team);
Profile.ProfileBuilder profileBuilderMock = Mockito.mock(Profile.ProfileBuilder.class);
when(profileBuilderMock.build()).thenReturn(profile);
verify(profileRepository, times(1)).save(profile);
}
}
But I am getting this error:
Wanted but not invoked:
profileRepository.save(
Profile(birthDate=25.07.1996, city=Mock for City, hashCode: 997294994, country=Mock for Country, hashCode: 506775047, imageId=imageId, team=Mock for Team, hashCode: 451959555)
);
-> at com.profile.profileservice.service.ProfileServiceImplTest.addProfileTest(ProfileServiceImplTest.java:31)
Actually, there were zero interactions with this mock.
What am I missing?
First, you are not calling addProfile() in your test. Also, you don't need to mock the ProfileBuilder here as Profile.builder() returns a new instance. It will not return the mocked instance.
Tip : use the given/when/then pattern for writing tests. This will help to not forget this kind of things.
#Test
void addProfileTest(){
// Given
Profile profile = new Profile("25.07.1996", city, country, "imageId", team);
// When
profileService.addProfile(profile);
// Then
verify(profileRepository, times(1)).save(profile);
}
This test passes.

How to specify a default assembler in seedstack?

I'm using the 19.11 version of seedstack, and I want to use the FluentAssembler assembler to convert an aggregate List into a DTO List.
I'm getting the following error when I call the fluentAssembler.assemble method :
org.seedstack.business.internal.BusinessException: [BUSINESS] Unable to find assembler
Description
-----------
No assembler was found to assemble 'com.inetpsa.svr.domain.model.customer.Customer(Customer.java:1)' to
'com.inetpsa.svr.interfaces.rest.customer.CustomerRepresentation(CustomerRepresentation.java:1)'.
Fix
---
Make sure that an assembler without qualifier exists. If you want to use a qualified assembler (like a default
assembler), specify its qualifier.
I don't know howto specify the qualifier, I'd like to use a default model mapper...
Here is The Resource code :
#Path("customers")
public class CustomerResource {
#Inject
private FluentAssembler fluentAssembler;
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<CustomerRepresentation> listAllCustomers() {
List<Customer> customerList = fetchAllCustomers();
return fluentAssembler.assemble(customerList).toListOf(CustomerRepresentation.class);
}
/**
* Test method - Should be replaced by a repository
* #return List<Customer> all customers
*/
private List<Customer> fetchAllCustomers(){
List<Customer> customerList = new ArrayList<>();
customerList.add(buildCustomer("005","Edward Teach","edward.teach#pirates.org"));
customerList.add(buildCustomer("006","Olivier Levasseur","olivier.levasseur#pirates.org"));
customerList.add(buildCustomer("007","James Bond","james.bond#mi6.uk"));
return customerList;
}
private Customer buildCustomer(String id, String name, String mail){
Customer result = new Customer(id);
result.updateNameAndMail(name, mail);
return result;
}
}
The aggregate :
public class Customer extends BaseAggregateRoot<String> {
#Identity
private String identifier;
private String name;
private String mail;
public Customer(String identifier){
this.identifier=identifier;
}
public void updateNameAndMail(String name, String mail){
if(StringUtils.isBlank(name)){
throw new IllegalArgumentException("Name can't be blank");
}
if(StringUtils.isBlank(mail)){
throw new IllegalArgumentException("Mail can't be blank");
}
this.name=name;
this.mail=mail;
}
public String getIdentifier() {
return identifier;
}
public String getName() {
return name;
}
public String getMail() {
return mail;
}
}
And the DTO :
#DtoOf(Customer.class)
public class CustomerRepresentation {
private String identifier;
private String name;
private String mail;
/**
* Required public no parameters constructor
*/
public CustomerRepresentation(){}
public CustomerRepresentation(String identifier, String name, String mail){
}
#AggregateId
public String getIdentifier() {
return identifier;
}
public String getMail() {
return mail;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public void setMail(String mail) {
this.mail = mail;
}
}
FluentAssembler only takes care of matching a Dto with an Assembler, but does not provide a default implementation of an Assembler by itself.
You have 2 Options to provide a Default Assembler.
Build a class that implements Asselmber
Include an addon that provides that Default Assember for you (As stated on the docs)

How to exclude value null from Put request when mapping to dto

I using RestController update data to db but I have problem. When i update value, if value from my update is null , it allways update data to db is null. I dont't want it. I want if 1 field with value is null from my request, i don't want update it.
This bellow my code :
Controller:
RestController
#RequestMapping("/api/products")
#Api(value = "ProductControllerApi",produces = MediaType.APPLICATION_JSON_VALUE)
public class ProductController {
#Autowired
private ProductService productService;
#PatchMapping("/{id}")
public ResponseEntity<ProductResDto> updateProduct(#RequestBody ProductReqDto productReqDto, #PathVariable String id) {
return ResponseEntity.ok(productService.updateProduct(product,id));
}
ProductReqDto:
public class ProductReqDto {
private String name;
private String type;
private String category;
private String description;
private Double prince;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getPrince() {
return prince;
}
public void setPrince(Double prince) {
this.prince = prince;
}
}
ProductResDto:
public class ProductResDto {
private String name;
private String type;
private String category;
private Double prince;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getPrince() {
return prince;
}
public void setPrince(Double prince) {
this.prince = prince;
}
}
MappingDto:
private ProductDto convertToProductDto(ProductReq product) {
return modelMapper.map(product, ProductResDto.class);
}
How to i handle method convertToProductDto only mapping with value not null. Because if , mapping one field : example : product_name = null , it insert to db null. I want if field ProductReq have value, it mapping and keep other different field in database(not set it null if not contain value from ProductReq) .
Example:
**ReqProductDto.class**
private String name;
private String type;
private String category;
private String description;
private Double prince;
but if user only update two field:
private String name;
private String type;
I want spring update field name, and field type user input and keep category,description,prince in my database. In my case, if user update two field: name, and field type,spring update it but spring set category,description,prince is null in my database. I don't want it.
Please help me, thanks.
You've tagged this as spring-boot, so I'm assuming you might be using controllers and validating their parameters. If that is the case, just do
import javax.validation.constraints.NotNull;
public class ProductReqDto {
#NotNull
private String name;
#NotNull
private String type;
#NotNull
private String category;
#NotNull
private String description;
#NotNull
private Double prince;
...
}
and use #Valid for your controllers like this
#PatchMapping("/{id}")
public ResponseEntity<ProductResDto> updateProduct(#RequestBody #Valid ProductReqDto productReqDto, #PathVariable String id) {
return ResponseEntity.ok(productService.updateProduct(product,id));
}
Then your object will be validated on instantiation.
What you want is mainly used for PATCH mapping.
IN a PUT mapping, all fields of an object need to override, but in a PATCH mapping only the fields which are provided needs to be overridden, others need not be changed.
So,
for an existing record,
employee{ employeeId = "A2RTD", empName = "satish", "country": "India"}
And, now one non-mandatory field mobileNo needs to be updated along with the country
DTO request will contain all field other than id, but only country & mobile no will not be null
In this scenario, we can use BeanUtils which is part of spring package
import org.springframework.beans.BeanUtils;
public static Object getDtoMapping(Object source, Object destination) {
BeanUtils.copyProperties(source, destination, getNullFieldNames(source));
return destination;
}
public static String[] getNullFieldNames(Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> fieldNames = new HashSet<>();
for (PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null)
fieldNames.add(pd.getName());
}
String[] result = new String[fieldNames.size()];
return fieldNames.toArray(result);
}
Ths function "getNullFieldNames" will return fieldNames which have value null. So, those fields will not be mapped, as per 3rd optional paramter in BeanUtils
And, you need to pass
// PATCH
EmployeeDao emp = findById(empCode);
emp = (EmployeeDao) getDtoMapping(empUpdateDto, emp);
Here, in BeanUtil copyProperties, 3rd param is optional. If you give it works for PATCH mapping, if you don't give it behaves as PUT mapping.
Since, for PUT mapping, ignoring null as same as not ignoring.
You can use the same in POST, PUT mapping also.
// POST MAPPING
EmployeeDao emp = (EmployeeDao) getDtoMapping(empCreateDto, new Employee());

difference between string and property String in tableview javafx

what is the difference between string and Property String in tableview javafx?
how it changes the tableview if i use data type as String or Property String ??
Can anyone give example to show this difference
Property String is different in Java. Basically you use property String when you want to observe your variable in a TableView. The reason Java does this is that Java uses an MVC Pattern (Model-View-Controller). The model is your stored data, the view is what you see like GUI and the controller is the brains and logic for everything in your application. The Model in Java is done as classes that hold properties rather than just fields. Because when you want to store data into a TableView in JavaFX the proper way is to instantiate objects from a class and the Properties defined in this class becomes the properties of this object, by then you can store the object in the TableView and put some logic to allow the tableView to go find the properties of this object and fill them in the table, if they were Strings rather than properties, JavaFX won't be able to get them and make them observable in the table. I wrote some logic below to give you an idea of how this is done. So first this is a class that acts as a Model:
public class Contact extends SQL_Objects {
private SimpleStringProperty id;
private SimpleStringProperty firstName;
private SimpleStringProperty lastName;
private SimpleStringProperty phone;
private SimpleStringProperty email;
private SimpleStringProperty unitNo;
private SimpleStringProperty street;
private SimpleStringProperty city;
private SimpleStringProperty province;
private SimpleStringProperty zipCode;
private SimpleStringProperty country;
private SimpleStringProperty gender;
private SimpleStringProperty notes;
private SimpleStringProperty relationship;
private final static String[] FIELD_NAMES = { "id", "firstName", "lastName", "phone", "email", "unitNo", "street", "city", "province", "zipCode", "country", "gender", "notes", "relationship" };
public Contact(String id, String firstName, String lastName, String phone, String email, String unitNo, String street, String city, String province, String zipCode, String country, String gender, String notes, String relationship) {
this.id = new SimpleStringProperty(id);
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
this.phone = new SimpleStringProperty(phone);
this.email = new SimpleStringProperty(email);
this.unitNo = new SimpleStringProperty(street);
this.street = new SimpleStringProperty(street);
this.city = new SimpleStringProperty(city);
this.province = new SimpleStringProperty(province);
this.zipCode = new SimpleStringProperty(zipCode);
this.country = new SimpleStringProperty(country);
this.gender = new SimpleStringProperty(gender);
this.notes = new SimpleStringProperty(notes);
this.relationship = new SimpleStringProperty(relationship);
}
public String getId() {
return id.get();
}
public String getFirstName() {
return firstName.get();
}
public String getLastName() {
return lastName.get();
}
public String getPhone() {
return phone.get();
}
public String getEmail() {
return email.get();
}
public String getUnitNo() {
return unitNo.get();
}
public String getStreet() {
return street.get();
}
public String getCity() {
return city.get();
}
public String getProvince() {
return province.get();
}
public String getZipCode() {
return zipCode.get();
}
public String getCountry() {
return country.get();
}
public String getGender() {
return gender.get();
}
public String getNotes() {
return notes.get();
}
public String getRelationship() {
return relationship.get();
}
public static String[] getFieldNames() {
return FIELD_NAMES;
}
Those getters and setters should follow the Standards of naming conventions in Java, so that when you insert and object into the table as i will show below, the table would use the field names and fetch for the getter for each field to get it's value and make it observable in the tabele, so below is an example of the controller to fill the columns and rows of the table:
private void fillColumns() {
try { // starting from 2 so that the id column is not included
for (int i = 2; i <= resultSet.getMetaData().getColumnCount(); i++ ) {
TableColumn column = new TableColumn(resultSet.getMetaData().getColumnName(i));
column.setCellValueFactory(new PropertyValueFactory<Contact, String>(Contact.getFieldNames()[i - 1]));
selectedTable.getColumns().add(column);
}
} catch (SQLException ex) {
Alert alert = new Alert(Alert.AlertType.ERROR, "Type:\n" + ex.getClass().getName() + "\n\nMessage: Unable to get the columns from the database\n\nDetails:\n" + ex.getMessage(), ButtonType.OK);
}
}
The next is to fill the rows, assuming i selected data from the database and stored them in a result set, i will use the next method to go through the records of the resultSet row by row, this method would return false when there is no more rows
private void fillRows() {
shownRecords = 0;
try {
while(resultSet.next()) {
Contact cont = new Contact(Integer.toString(resultSet.getInt(1)), resultSet.getString(2), resultSet.getString(3), resultSet.getString(4), resultSet.getString(5), resultSet.getString(6), resultSet.getString(7), resultSet.getString(8), resultSet.getString(9), resultSet.getString(10), resultSet.getString(11), resultSet.getString(12), resultSet.getString(13), resultSet.getString(14));
tableView.getItems().add(cont);
}
resultSet.beforeFirst();
} catch (SQLException ex) {
Alert alert = new Alert(Alert.AlertType.ERROR, "Type:\n" + ex.getClass().getName() + "\n\nMessage: Unable to get the records from the database\n\nDetails:\n" + ex.getMessage(), ButtonType.OK);
alert.show();
}
}
So as you can see, i used SimpleStringProperty rather than Strings and if u used Strings here JavaFx wouldn't be able to display the results as they somehow are not considered properties of the objects inserted into the table

Firebase DataSnapshot Not Filling Object

I have an object in a firebase database that I am trying to extract from a snapshot.
The JSON object from firebase is:
"-KoVHZ8YVll0RiI2GwKb" : {
"name" : "Name 1",
"phone_number" : "4443335555"
}
I am trying to safe it in an object that looks like this
public class Contact {
private String mName;
private String mPhoneNumber;
public Contact() {
mName = "";
mPhoneNumber = "";
}
public String getName() { return mName; }
public String getPhoneNumber() { return mPhoneNumber; }
public void setName(final String name) { mName = name; }
public void setPhoneNumber(final String phoneNumber) { mPhoneNumber = phoneNumber; }
}
I am calling
Contact contact = snapshot.getValue(Contact.class);
The contact object only has the name populated and not the phone number. The documentation just states that there must be public getters and an empty constructor in order for this to work. My guess is something is wrong with my naming convention anyone have any ideas?
EDIT
I am aware that I can extract the data out by doing this:
mName = (String) snapshot.child("nane").getValue();
mPhoneNumber = (String) snapshot.child("phone_number").getValue();
But then what is the point of creating the P.O.J.O.?
Make your Contact.java like this:
#IgnoreExtraProperties
public class Contact {
private String name;
private String phone_number;
public Contact() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone_number() {
return phone_number;
}
public void setPhone_number(String phone_number) {
this.phone_number = phone_number;
}
}

Categories

Resources