Converting Datatable to object with null values - testing

Hi Everyone just wondering if I am following best practices here.
I have step defintions like the following
public class StepDefinitions {
#DataTableType
public Author authorEntry(Map<String, String> entry) {
return new Author(
entry.get("firstName"),
entry.get("lastName"),
entry.get("famousBook"));
}
#Given("There are my favorite authors")
public void these_are_my_favourite_authors(List<Author> authors) {
// step implementation
}
}
and my feature file could then be something like
Feature: this is a feature
Scenario: this is a scenario
Given There are my favorite authors
|firstName| lastName |
| first | last |
Scenario: this is another scenario
Given There are my favorite authors
|firstName| lastName | famousBook |
| first | last | book |
So in the first step it will create an Author object but with famousBook == null.
Since I am creating objects used for REST requests and jackson will ignore null values is it okay to create objects like this?

You are using wrong data structure for your examples. You can refer to this resource giving the examples of different table types.
In your case you have a table
|firstName| lastName |
| first | last |
and try to parse it into Map<String, String> entry which would result in the following map:
[key1 = firstName, value1 = lastName]
[key2 = first, value2 = last]
If you need to treat it like header in top line then you need to parse it into List<Map<String, String>>

Related

Keep a relation map in Objection.js while removing the table

I'm developing a reddit-like site where votes are stored per-user (instead of per-post). Here's my relevant schema:
content
id | author_id | title | text
---|-----------|-------------|---
1 | 1 (adam) | First Post | This is a test post by adam
vote: All the votes ever voted by anyone on any post
id | voter_id | content_id | category_id
---|-------------|------------------|------------
1 | 1 (adam) | 1 ("First Post") | 1 (upvote)
2 | 2 (bob) | 1 ("First Post") | 1 (upvote)
vote_count: Current tally ("count") of total votes received by a post by all users
id | content_id | category_id | count
---|------------------|--------------|-------
1 | 1 ("First Post") | 1 (upvote) | 2
I've defined a voteCount relation in Objection.js model for the content table:
class Content extends Model {
static tableName = 'content';
static relationMappings = {
voteCount: {
relation: Model.HasManyRelation,
modelClass: VoteCount,
join: {
from: 'content.id',
to: 'vote_count.content_id'
}
}
}
}
But I recently (learned and) decided that I don't need to keep (and update) a separate vote_count table, when in fact I can just query the vote table and essentially get the same table as a result:
SELECT content_id
, category_id
, COUNT(*) AS count
FROM vote
GROUP
BY content_id
, category_id
So now I wanna get rid of the vote_count table entirely.
But it seems that would break my voteCount relation since there won't be a VoteCount model (not shown here but it's the corresponding the model for the vote_count table) no more either. (Right?)
How do I keep voteCount relation while getting rid of vote_count table (and thus VoteCount model with it)?
Is there a way to somehow specify in the relation that instead of looking at a concrete table, it should look at the result of a query? Or is it possible to define a model class for the same?
My underlying database in PostgreSQL if that helps.
Thanks to #Belayer. Views were exactly the solution to this problem.
Objection.js supports using views (instead of table) in a Model class, so all I had to do was create a view based on the above query.
I'm also using Knex's migration strategy to create/version my database, and although it doesn't (yet) support creating views out of the box, I found you can just use raw queries:
module.exports.up = async function(knex) {
await knex.raw(`
CREATE OR REPLACE VIEW "vote_count" AS (
SELECT content_id
, category_id
, COUNT(*) AS count
FROM vote
GROUP
BY content_id
, category_id
)
`);
};
module.exports.down = async function(knex) {
await knex.raw('DROP VIEW "vote_count";');
};
The above migration step replaces my table vote_count for the equivalent view, and the Objection.js Model class for it (VoteCount) worked as usual without needing any change, and so did the relation voteCount on the Content class.

Laravel | Polymorphic Relationship unknown column

This is my first time using a polymorphic relationship.
I am creating a LMS where a Assignment can be allocated to a individual user or a team so reading the Laravel docs it seems that the Polymorphic Relationship will be a good way to go about it.
I have created 4 tables.
Users:
| id | username | password | created_at | updated_at |
Teams: | id | friendly_name | slug |
Team User: | id | user_id | team_id |
Assignment Allocation:
| id | assignment_id | assignmentable_type | assignmentable_id | created_at | updated_at
So when the assignment_allocations has data in it looks like this...
| id | assignment_id | assignmentable_type | assignmentable_id |
| 1 | 1 | App\Models\User | 1 |
However I get this error:
SQL: select * from users where users.assignmentable_id = 1 and users.assignmentable_id is not null and users.assignmentable_type = App\Models\User
SO obviously I have done something wrong, however I cannot for the life of me figure out what I've done wrong.
This is my functions that relate to this:
AssignmentAllocation.php
public function assignmentable()
{
return $this->morphTo();
}
User.php
public function assignments()
{
return $this->morphMany('App\Models\User', 'assignmentable');
}
Team.php
public function assignments()
{
return $this->morphMany('App\Model\Team', 'assignmentable');
}
Any help is greatly appreciated.
There are 2 potential answers depending on your data structure, which isn't clear from your question.
User/Team to Assignment is a one-to-many relationship (each Assignment has one User/Team, and each User/Team has many Assignments
Many-to-many relationship where each User/Team has many Assignments and each Assignment has many Users/Teams
One to many
In a one-to-many you wouldn't need an Assignment table and an AssignmentAllocation table, so I am assuming your AssignmentAllocation is your Assignment model. If not then you need to put the assignmentable_type and assignmentable_id columns on the assignments table instead, and use Assignment.php instead of AssignmentAllocation.php.
AssignmentAllocation.php
public function assignmentable()
{
return $this->morphTo();
}
User.php
public function assignments()
{
return $this->morphMany('App\Models\AssignmentAllocation', 'assignmentable');
}
Team.php
public function assignments()
{
return $this->morphMany('App\Models\AssignmentAllocation', 'assignmentable');
}
Your error is because Laravel is searching the users table for the match (you have morphMany('User'..), when it should be searching the AssignmentAllocation table. So just switch them out.
1) The morphMany acts like a hasMany, so you're saying:
Each User hasMany Assignments, and each Team hasMany Assignments. The first part of the morphMany says "search the AssignmentAllocation table", and the second part says "search for assignmentable_id and assignmentable_type being equal to this instance of this model".
2) morphTo acts like belongsTo so you're saying:
Each Assignment belongsTo an assignmentable.
Many to many
However if AssignmentAllocation is a many-to-many pivot table and each Assignment has many Users or Teams, and each User/Team has many Assignments, then you need a morphToMany/morphedByMany pair.
User.php
public function assignments()
{
return $this->morphToMany('App\Models\Assignment', 'assignmentable');
}
Team.php
public function assignments()
{
return $this->morphToMany('App\Models\Assignment', 'assignmentable');
}
Assignment.php model NOTE: not the AssignmentAllocation model
public function users()
{
return $this->morphedByMany('App\Models\User', 'assignmentable');
}
public function teams()
{
return $this->morphedByMany('App\Models\Team', 'assignmentable');
}
You should rename the assignment_allocation table to assignmentables, or add the required third and fourth arguments to the morph functions. I prefer to keep the table names consistent.

how to dynamically set an value in json read from file in Karate

I want to dynamically set value for some elements in JSON(read from a file) using data driven feature of KARATE framework. Here are more details:
request.json -> { wheels : <wheel>, color: '<color>' }
Feature: Read json input from file and iterate over data table values
Background:
* url ''
* def reqJson = read('request.json')
* print reqJson
Scenario Outline: Test file read
# I want to avoid writing below set statements for each element in request
#* set reqJson.wheels = <wheel>
#* set reqJson.color = '<color>'
Given path ''
And request reqJson
When method POST
Then status 200
And match response contains {mode: '<result>'}
Examples:
| wheel | color | result |
| 4 | red | car |
| 2 | any | bicycle |
I am developing automation framework using Karate, my intention is to save sample request in JSON file for a given API and then during execution I want element values to be replaced with the ones given in the table above.I don't want to write set statement for each element either(commented lines above)
P.S.: I tried with calling other feature file using table approach. However, I want to keep one feature file per API, hence want to know if there is any way possible for the above approach
I think you have missed embedded expressions which is simpler than the set keyword in many cases, especially when reading from files.
For example:
request.json -> { wheels : '#(wheels)', color: '#(color)' }
And then this would work:
* def wheels = 4
* def color = 'blue'
* def reqJson = read('request.json')
* match reqJson == { wheels: 4, color: 'blue' }
If you go through the demo examples you will get plenty of other ideas. For example:
* table rows
| wheels | color | result |
| 4 | 'blue' | 'car' |
| 2 | 'red' | 'bike' |
* call read('make-request.feature') rows
And where make-request.feature is:
Given path ''
And request { wheels: '#(wheels)', color: '#(color)' }
When method POST
Then status 200
And match response contains { mode: '#(result)' }

SQL / (Django) : efficient database schema for translations

Situation
I trying to set up a database schema to store translations, between different languages. So far it looks like this (simplyfied):
class Language(models.Model):
tag = models.CharField(max_length=2)
def __unicode__(self):
return self.tag
class Phrase(models.Model):
name = models.TextField()
language = models.ForeignKey(Language)
def __unicode__(self):
return self.name
class Meta:
unique_together = ("name", "language")
index_together = [
["name", "language"]
]
class Translation(models.Model):
phrase1 = models.ForeignKey(Phrase, related_name="translation_as_1")
phrase2 = models.ForeignKey(Phrase, related_name="translation_as_2")
def __unicode__(self):
return self.phrase1.name + " <=> " + self.phrase2.name
class Meta:
unique_together = ("phrase1", "phrase2")
index_together = [
["phrase1", "phrase2"]
]
This database schema seems logical to me. I store phrases in different languages and then have translations that contain exactly two phrases.
Problem
The problem is, that the queries, that result out of this schema, look kind of nasty. For instance:
from django.db.models import Q
name = "my phrase"
translations = Translation.objects.filter(Q(phrase1__name=text)|Q(phrase2__name=text))
translated_names = []
for translation in translations:
name1 = translation.phrase1.name
name2 = translation.phrase2.name
if name1 == name:
translated_names.append(name2)
else:
translated_names.append(name1)
I always have to include the "OR" relationship, to make sure, that I get all the possible translations, since the phrase could be stored as phrase1 or phrase2. On top of that, I have to filter my result afterwards to get the correct translated_name (for loop).
Further Explaination
Before I switched to the described schema, I had the following schema instead (Phrase and Language are the same as before):
class Translation(models.Model):
phrase = models.ForeignKey(Phrase)
name = models.TextField()
def __unicode__(self):
return self.phrase.name + " => " + self.name
class Meta:
unique_together = ("phrase", "name")
index_together = [
["phrase", "name"]
This schema let me make queries like this:
from django.db.models import Q
name = "my phrase"
translations = Translation.objects.filter(phrase__name=text)
translated_names = [t.name for t in translations]
This looks much nicer, and is of course faster. But this schema had the disadvantage, that it presents translations only in one direction, so I moved to the other one, which isn't quite what I want as well, because too slow and too complicated queries.
Question
So is there a good schema for this kind of problem, that I maybe overlook?
Remark
I'm not only interested in Django related answers. A pure SQL schema for this kind of problem would also be interesting for me.
This is the way that I have done it in the past. Adapt it for your naming convention.
Suppose that I had a table with a name and other columns in it like this
Table TR_CLT_clothing_type
clt_id | clt_name | other columns ....
--------------------------------------
1 | T Shirt ...
2 | Pants ...
Now if I decided that it needs translations, first I make a languages table
Table TR_LNG_language
lng_id | lng_name | lng_display
-------------------------------
1 | English | English (NZ)
2 | German | Deutsch
I also need to store the current language in the database (you will see why soon). It will only have one row
Table TA_INF_info
inf_current_lng
---------------
1
Then I drop the clt_name column from my clothing table TR_CLT_clothing_type. Instead I make relation table.
Table TL_CLT_clothing_type
clt_id | lng_id | clt_name
--------------------------
1 | 1 | T Shirt
1 | 2 | (German for T-Shirt)
2 | 1 | Pants
2 | 2 | keuchen (thank you google translate)
Now to get the name, you want to make a stored procedure for it. I have not attempted this in ORM.
CREATE PROCEDURE PS_CLT
#clt_id int
AS
SELECT lng.clt_name, clt.*
FROM TR_CLT_clothing_type clt
JOIN TL_CLT_clothing_type lng
ON lng.clt_id = clt.clt_id
WHERE clt.clt_id = #clt_id AND
lng.lng_id in (SELECT inf_current_lng FROM TA_INF_info)
This stored proc will return the name in the current language and all other columns for a specified language. To set the language, set the clt_current_lng in the TA_INF_info table.
Disclaimer: I don't have anything to check the syntax of what I have typed but it should hopefully be straightforward.
-- EDIT
There was a concern to be able to do "give me all translations for word X in language Y to language Z"
There is a "not so elegant" way to do this with the schema. You can do something like
for each table in database like "TL_%"
SELECT name
FROM table
WHERE id IN ( SELECT id
FROM table
WHERE name = #name
AND lng_id = german
)
AND lng_id = english
Now I would imagine that this would require some auto-generated SQL code but I could pull it off.
I have no idea how you would do this in ORM

Django: ManyToManyField with additional Column

I am trying to create a job application-form with Django.
Basically, I created two models.
softwareskill_model
application_model
The admin can log into the admin-section and add new softwareskill-
entries to the database. The application_model references those
softwareskill-entries/records using a ManyToMany-Field:
class softwareskill_model(django.db.models.Model):
name = django.db.models.CharField(max_length=200)
class application_model(django.db.models.Model):
# ...
softwareskills = django.db.models.ManyToManyField(softwareskill_model)
So if someone wants to apply for the job, he can select which
software-packages he uses.
Now I want the applicant to make a rating from 1-6 for each software-skill
he has selected. How do you do that?
I am using a SQLite3 database and discovered that the ManyToManyField
creates a new table to store the relationship. In my case it looks like
this:
| ID | application_model_id | softwareskill_model_id |
My assumption would be to simply add a new column so it looks like this:
| ID | application_model_id | softwareskill_model_id | Rating |
Is that possible / the best way to do it? How?
I am very new to Django, databases and web-development in general and hope
you can help me :-)!
Thank you,
Henry
through is what you need to use, e.g.
class softwareskill_model(django.db.models.Model):
name = django.db.models.CharField(max_length=200)
class application_model(django.db.models.Model):
# ...
softwareskills = django.db.models.ManyToManyField(softwareskill_model, through="ApplicationSoftwareSkill")
class ApplicationSoftwareSkill(models.Model):
softwareskill = models.ForeignKey(softwareskill_model)
application = models.ForeignKey(application_model)
# extra fields here e.g.
rating = models.IntegerField()