My app is receiving payload and I want to make sure it's recorded only once.
How can I create a uniqueness?
If the same publication is sent twice it shouldn't be saved.
payload example
{
"data": {
"type": "publication",
"attributes": {
"advert": {
"title": Super cat",
"description": "Some dummy text",
"customer": {
"name": "Mozart"
}
}
}
}
}
Finally found what expected
validates :customer, uniqueness: {scope: :title}
If you want to make sure your payload is unique and only saved once, then you need to assign a field in your model to be unique. From the JSON example you provide, and stating that the same publication shouldn't be saved twice, one option is to make the title unique.
Your question is tagged Ruby on Rails, so you could use the following in your model to enforce this:
validates :title, uniqueness: true
Unless you are provided with an ID (as mentioned in the comments) or some other independently unique identifier, you need to pick a field returned in the payload to check against.
Related
In my Rails 7 app I'm receiving request from the external API. I want to check if the incoming request is valid. To do so I've to use strong parameters, here is the sample JSON request which hit my endpoint:
"sdd_request": {
"return_url": "https://example.com/return",
"data": {
"debit_method": "CORE",
"debtor": {
"name": "John Doe",
"email": "john.doe#example.com",
}
},
"extension": {
"signees": [{email: 'test#test.com', name: 'joe', last_name: 'smith' }],
"creditor": {
"id": "12345",
"name": "Acme Inc.",
"address": {
"city": "New York",
}
}
}
}
}
So the required are:
- return_url
- data
- signees
- creditor
How to require above parameters?
What I did was:
def sdd_setup_request_params
params.require(:sdd_request).permit(
:return_url,
data: [
:debit_method,
debtor: [
:name,
:email,
]
],
extension: [
signees: [],
creditor: [
:id,
:name,
address: [
:city,
]
]
]
]
)
end
But from what I understand that required whole object of sdd_request not individual components because if e.g. signees are missing it will nor raise an error of missing parameters am I right?
The role of strong parameters is not to validate the data. It's just to whitelist the parameters to avoid mass assignment vulnerabilities.
require is used to bail early if the general structure of the parameters makes it pointless to continue processing the request. Like for example in your typical Rails controller:
params.require(:person)
.permit(:name, :age, :city)
There is no point in continuing to process the request if the key :person is missing so a ActionController::ParameterMissing exception is raised which Rails rescues and returns a 400 - Bad Request response.
This prevents the potential nil errors that could occur when you expect a hash and get nil instead.
Validating the presence of the actual attributes is the job of the model in Rails.
class Person
include ActiveModel::Model
include ActiveModel::Attributes
attribute :name
attribute :age
attribute :city
validates :name, :age, :city, presence: true
end
You might be thinking now: But waaaah I don't have a model because I'm gettting it from an API and I'm not saving it. Models can still be really useful even without persistence since the represent the entities in your application in a normalized form.
If you still really want to avoid a model for whatever reason there are gems that provide model-less validation in the controller with a bit more grace then a web of if statements.
While you can use require on nested hash structures like JSONAPI.org this will just return a single key and will raise on the first missing key so it cannot be used to give meaningful feedback about whats wrong with the input.
params.require(:data)
.require(:attributes)
.permit(:name, :age, :city)
I've been able to set up Active Storage file uploads and now I'm trying to return associated images when I do, for instance, Speaker.all or Speaker.find(2).
Calling the associated endpoint I get something like:
{
"id": 2,
"name": "Rafael",
"email": "rafael.almeida#mail-provider.com",
"company": "XING",
"social_media": "{\"twitter\": \"#rafaelcpalmeida\"}",
"created_at": "2018-10-01T17:21:50.993Z",
"updated_at": "2018-10-01T17:21:51.144Z"
}
How can I also return its associated avatar?
I figured out what to do in order to achieve the result I wanted. First, we need to add the active_model_serializers to the Gemfile, followed by bundle install.
After we installed the gem we should add include ActionController::Serialization to every controller that's going to use the Serializer.
We generate a new serializer using rails g serializer speaker. My SpeakerSerializer looks like:
class SpeakerSerializer < ActiveModel::Serializer
attributes :id, :name, :email, :company, :avatar
def avatar
rails_blob_path(object.avatar, only_path: true) if object.avatar.attached?
end
end
And my output looks like
{
"speaker": {
"id": 2,
"name": "Rafael",
"email": "rafael.almeida#xing.com",
"company": "XING",
"avatar": "/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCdz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--515a0de8817b3529b5d3d168871cebf6ccee0463/xing-photo.jpg"
}
}
Check if this approach is good for your needs. This is the case of has_one_attached.
First, fetch the record:
speaker = Speaker.find(2)
Then convert it to a Ruby hash (please note .as_json):
speaker_hash = speaker.as_json
Now, just append the pair key-value you need, using url_for helper:
speaker_hash['url'] = url_for(speaker.avatar)
Finally, convert the hash to json:
speaker_hash.to_json
I have a Rails 5 app in which I use the gem active_model_serializers(https://github.com/rails-api/active_model_serializers). In my app I have a simplified data model that looks something like this:
# LocalizedString.rb
has_many :translations
# Translation.rb
belongs_to :localized_string
I'm trying to follow the best practices from JSON API, I have configured active_model_serializers like this:
ActiveModelSerializers.config.adapter = :json_api
When a user of the API requests translations (http://[root]/api/apps/117/translations) I currently get the following result:
{
"data": [
{
"id": "152",
"type": "translations",
"attributes": {
"value": "Test",
},
"relationships": {
"language": {
"data": {
"id": "1",
"type": "languages"
}
},
"localized-string": {
"data": {
"id": "162",
"type": "localized-strings"
}
}
}
},
[...]
From my localised-string I also want to include another attribute that is critical for the consumer of the API, and I don't want to have to make another API call to get the value of the attribute. I wonder what is the best / recommended way to do this that also follows json_api if possible.
Something like this could work:
"localized-string": {
"data": {
"id": "162",
"key": "my key value", # the attribute I need.
"type": "localized-strings"
}
}
But I'm not sure how to achieve that using active_model_serializers or if it is another recommended way of doing what I want with [json_api][1].
For completion, my relevant serialiser files looks lik this:
class TranslationSerializer < ActiveModel::Serializer
attributes :id, :value, :created_at, :updated_at
has_one :language
has_one :localized_string, serializer: LocalizedStringParentSerializer
end
class LocalizedStringParentSerializer < ActiveModel::Serializer
# I want to include the key attribute in my relationship object, but this doesn't work.
attributes :id, :key
end
So, any ideas on what I need to do to achieve what I want?
Per spec, relationships are represented by resource object identifiers. To include more than just the id and type, you'll want to use the include param. In AMS, I think that would be 'include: [:localizations], fields: { localizations: [:key]}' (not at computer now, but is approx right)
I'm currently calling this every 3hrs, but it's storing duplicates of the same data. I've tried a few methods here on Stackoverflow but none of them seem to be actually working.
def fetch_data
response = self.class.get("/tap-2_JD7rkZupwNmg91im5G/last_ready_run/data?format=json")
#elements = response.parsed_response["link"]
# To access the image src's:
image_srcs = #elements.collect { |e| e['image'] }
image_srcs.each do |image|
self.entries.create(image: image)
end
end
Is there a way to check against each 'image' string that it collects and make sure it's not a duplicate before it inserts a new entry into the database
Thanks
Edit - Example Response
{
"link": [
{
"link": "http://www.test.com",
"image": "http://www.test.com/500x500.jpg"
}, ....
Use Rails build-in validation: validate :image, :uniqueness. (http://guides.rubyonrails.org/active_record_validations.html#uniqueness)
Note: beware that it's not 100% protection from duplicates. For more robust solution please use DB level constraints (ex. add_index :entries, :image, unique: true in migrations)
I am developing a Ruby on Rails (3.2.6) application and is using MongoId (3.0.0) to interact with the MongoDB database. I am just wondering how do save embeded JSON objects that contains multiple levels and not just one level.
I got an old MongoDB database with this and simular structure so I need to save new documents using the same structure.
This is from the documentation and is used to add a one level document:
Person.create(
first_name: "Heinrich",
last_name: "Heine"
)
How can I add an object with this structure:
{
"basic": {
"file_id": {
"file": "cf1952761a806c56c9bee60665418f02c"
},
"share": false,
"status": "created"
},
"data": {
"id": "4fd942dder5f5e88837300026e",
"name": "roberta",
"comment": "This is a comment"
}
}
The easiest way to do this is to create classes for basic and data and embed them in your top level document.
Embedded document classes are defined in Mongoid the same way as other documents with an embedded_in call and a matching embeds_one or embeds_many in the top level document.
The other option is to simply define a Hash field, but this obviously may have any structure.
Class Person
include Mongoid::Document
field :data, :type => Hash
...
end
:data will accept any hash, even with nested hashes.