Is it possible to define properties in a Swagger/OpenAPI definition that can be one of two types.
For example, our API allows a source ID to be sent as a string, or a source object. The source object has a fixed schema:
Source ID:
{
"source": "src_123"
}
Source Object:
{
"source": {
"foo": "bar"
}
}
I am unsure how to represent this in my Swagger definition.
Related
I'm new to MongoDB as well as to MongoDB Realm Sync. I was following the Realm Sync tutorial and Realm data model docs, but I wanted to learn more so I tweaked the Atlas collection structure as follows.
Projects > Tasks // i.e. tasks is a sub-collection in each project.
What I don't know is how to come up with Realm Sync Schema which can support Atlas sub-collections.
The best I came up with is a Schema where Tasks are modelled as an array within the Project. But, I'm worried that this can hit the 16MB (although a lot!) document limit for projects with a lot of the tasks.
{
"bsonType": "object",
"properties": {
"_id": {
"bsonType": "objectId"
},
"_partition": {
"bsonType": "string"
},
"name": {
"bsonType": "string"
},
"tasks": {
"bsonType": "array",
"items": {
"bsonType": "object",
"title": "Task",
"properties": {
"name": {
"bsonType": "string"
},
"status": {
"bsonType": "string"
}
}
}
}
},
"required": [
"_id",
"_partition",
"name",
],
"title": "Project"
}
Looking forward on how to model sub-collection the right way.
Edit
Here's my client side Realm models.
import Foundation
import RealmSwift
class Project: Object {
#objc dynamic var _id: String = ObjectId.generate().stringValue
#objc dynamic var _partition: String = "" // user.id
#objc dynamic var name: String = ""
var tasks = RealmSwift.List<Task>()
override static func primaryKey() -> String? {
return "_id"
}
}
class Task: EmbeddedObject {
#objc dynamic var name: String = ""
#objc dynamic var status: String = "Pending"
}
As far the CRUD operations are concerned, I only create a new project and read existing projects as follows.
// Read projects
realm.objects(Project.self).forEach { (project) in
// Access fields
}
// Create a new project
try! realm.write {
realm.add(project)
}
Your code looks great and your heading the right direction, so this answer is more explanation and suggestions on modeling than hard code.
First, Realm objects are lazily loaded which means they are only loaded when used. Tens of thousands of objects will have very little impact on a devices memory. So suppose you have 10,000 users and you 'load them all in'
let myTenThousandUsers = realm.objects(UserClass.self)
meh, no big deal. However, doing this
let someFilteredUsers = myTenThousandUsers.filter { $0.blah == "blah" }
will (could) create a problem - if that returns 10,000 users they are all loaded into memory possibly overwhelming the device. That's a Swift function and 'converting' Realms lazy data using Swift should generally be avoided (use case dependent)
The observation of this code using Swift .forEach
realm.objects(Project.self).forEach { (project) in
// Access fields
}
could cause issues depending on what's being done with those project objects - using them as a tableView dataSource could be trouble if there are a lot of them.
Second thing is the question about the 16Mb limit per document. For clarity an Atlas document is this
{
field1: value1,
field2: value2,
field3: value3,
...
fieldN: valueN
}
where value can be any of the BSON data types such as other documents, arrays, and arrays of documents.
In your structure, the var tasks = RealmSwift.List<Task>() where Task is an embedded object. While conceptually embedded objects are objects, I believe they count toward a single document limit because they are embedded (correct me if I am wrong); as the number of them grows, the size of the enclosing document grows - keeping in mind that 16Mb of text is an ENORMOUS of text so that would/could equate to millions of tasks per project.
The simple solution is to not embed them and have them stand on their own.
class Task: Object {
#objc dynamic var _id: String = ObjectId.generate().stringValue
#objc dynamic var _partition: String = ""
#objc dynamic var name: String = ""
#objc dynamic var status: String = "Pending"
override static func primaryKey() -> String? {
return "_id"
}
}
Then each one can be 16Mb, and an 'unlimited number' can be associated with a single project. One advantage of embedded objects is a type of cascade delete where when the parent object is deleted, the child objects are as well, but with a 1-many relationship from Project to Tasks - deleting a bunch of tasks belonging to a parent is easy.
Oh - another case for not using embedded objects - especially for this use case - is they cannot have indexed properties. Indexing can greatly speed up some queries.
#Field(index = FieldIndex.analyzed, type = FieldType.String)
How to add annotation here to disable norms for the analysed field
As there is no way to add norms enable-false attribute as part of #field annotations in java entity, we can add all mappings (all required types with attributes as mappings) in mappings.json file and refer this file in entity file. Like as below
#Document(indexName = "jobindex")
#Setting(settingPath = "/config/elasticsearch-settings.json")
#Mapping(mappingPath = "/config/mappings.json") //THIS ONE TO ADD
public class JobIndex implements Serializable {
}
and mappings.json look like
"mappings": {
"_doc": {
"properties": {
"title": {
"type": "text",
"norms": { "enabled": false }
}
}
}
}
NOTE: when you add specific attributes as part of mappings.json which are not available in java #Field annotations then better add all field annotations part of json file rather than in java#Field annotations. So the conclusion is java entity should be without field annotations and all mappings should be in mappings.json file and that file should be referred in entity header as mentioned in the first code block of this answer.
I tried the following code in Avro IDL which references the logical type timestamp-millis and it doesn't work.
Is there an import required to use logical types in Avro IDL? Or are logical types not useable, and I need to use the primitive type (long in this case) instead?
protocol test {
record test {
timestamp-millis time;
}
}
Results in:
Exception in thread "main" org.apache.avro.compiler.idl.ParseException: Undefined name 'timestamp', at line 3, column 9
This works of course:
protocol test {
record test {
long time;
}
}
You can use a generic annotation:
protocol test {
record test {
#logicalType("timestamp-millis")
long time;
}
}
There's actually also a shorthand you can use for timestamp-millis and a handful of other logical types (the documentation hasn't been released yet, see here for the full list of aliases):
protocol test {
record test {
timestamp_ms time;
}
}
LogicalType timestamp-millis is used with type long, so you may use schema like this:
{
"type" : "record",
"name" : "Test",
"fields" : [ {
"name" : "time",
"type" : {
"type" : "long",
"logicalType" : "timestamp-millis"
}
}]
}
I want to add a HAL resource linking to a definition,
definitions
HalItemResponse:
name:
type: string
What to add
"_links": {
"self": string,
"filter": string
}
How?
Where ever you want to use the definition, typically in a request or response you'd reference it like so.
JSON
{
halitem: { $ref: '#/defintions/HalItemResponse' }
}
YAML
halitem:
$ref: '#/definitions/HalItemResponse'
Very similar to setting the type but instead pointing to the definition schema.
var myObjectList = (List<MyObject>)JsonConvert.DeserializeObject(strResponseMessage, typeof(List<MyObject>));
the above works to deserialise a JSON string to a list of custom objects when the JSON has the following format
[
{
"Name": "Value"
},
{
"Name": "Value"
},
{
"Name": "Value"
},
"Name": "Value"
}
]
I don't know how to do the same when the format is like this
{
"ReturnedData" : [
{
"Name": "Value"
},
{
"Name": "Value"
},
{
"Name": "Value"
},
"Name": "Value"
}
]
}
I can get the data like this
JObject information = JObject.Parse(strResponseMessage);
foreach (dynamic data in information)
{
//convert to object here
}
and that works for Android but it seems that you cannot use a type of 'dynamic' for iOS as I get the error:
Object type Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder cannot be converted to target type: System.Object[]
What step am I missing to convert the second JSON string to the first?
If JsonConvert is JSON.Net just instead of List use
public class MyClass {
public List<MyObject> ReturnedData { get; set; }
}
You can't use the dynamic keyword on iOS as its forbidden to generate code as it states in this link.
Quote:-
No Dynamic Code Generation
Since the iPhone's kernel prevents an application from generating code dynamically Mono on the iPhone does not support any form of dynamic code generation.
These include:
The System.Reflection.Emit is not available.
Quote:-
System.Reflection.Emit
The lack of System.Reflection. Emit means that no code that depends on runtime code generation will work. This includes things like:
The Dynamic Language Runtime.
Any languages built on top of the Dynamic Language Runtime.
Apparently there is some support creeping in from v7.2 as can be seen in this link - See #Rodja answer. - however - its very experimental and has flaws preventing this from fully working.
Your best approach would be to process the JObject - without - reying on the dynamic keyword and you will be alright on both Android and iOS.
Thanks for your replies - I was able to solve it as follows
JObject information = JObject.Parse(strResponseMessage);
string json = JsonConvert.SerializeObject(strResponseMessage["ReturnedData "]);
var myObjectList = (List<MyObject>)JsonConvert.DeserializeObject(json , typeof(List<MyObject>));
Works perfectly!