Need help doing a many <-> many match with Fluent in Vapor 3 - vapor

I've got a Vapor 3 app in which I need to determine is 2 users share any common groups.
So I have 2 models User and Group. A user can belong to many groups. But I have a permission check that if both users share a common group they can send a messages to each other.
final public class User: PostgreSQLModel {
public var id: Int?
var firstName: String
var lastName: String
}
extension User {
var containers: Siblings<User, Group, UserGroups> {
return siblings()
}
}
final public class Group: PostgreSQLModel {
public var id: Int?
var name: String
}
What I'd like to do is this
UserGroups.query(to: request).group(.or) { $0.filter(\.userId == user.id!).filter(\.userId == user.id!) }.flatMap { ... }
plus a filter that says where groupId == groupId.
Any thoughts or suggestions?

I don't think you can do it in a single query, but you could try something like:
UserGroups.query(on:request).filter(\.userId == user1.id!).all().flatMap
{
groups in
groups.map { $0.id! }.flatMap
{
groupIds in
UserGroups.query(on:request).filter(\.userId == user2.id!).filter(\.groupId ~~ groupIds).all().flatMap
{
// do your work here
}
}
}

Related

Protocols and Enums in Swift with Apollo

I am using Apollo for Swift in an iOS app. I have multiple types that all represent the same object. These types are auto-generated from a schema file and look something like this.
struct CurrentUser {
var id: String
...
}
struct MyUser {
var id: String
...
}
Basically Apollo generates multiple Swift types (one for each query) for the same underlying data type.
I want to create a new struct that unifies these types.
I would like to do something like this:
protocol UserProtocol {
var id: String { get }
}
struct User {
var id: String
...
init(_ data: UserProtocol) {
self.id = data.id
...
}
}
This approach however gives me an error when I try to construct a user object, telling me that "Type MyUser does not conform to UserProtocol". If I try to coerce the type with data as! UserProtocol I get a crash.
The only solution I've found is the following:
enum UserType {
case .currentUser(CurrentUser)
case .myUser(MyUser)
}
struct User {
var id: String
...
init(_ data: UserType) {
switch data {
case .myUser(let user):
self.id = data.id
...
case .currentUser(let user):
self.id = data.id
...
}
}
}
This approach works, but it leads to a lot of duplicated code in the init function. Is there a better way to do this in Swift?
I suspect the problem is that you need to explicitly conform the Apollo generated types to your protocol:
extension CurrentUser: UserProtocol { }
extension MyUser: UserProtocol { }
Remember that Swift is not duck-typed like some other languages, so a type with member var id: String is not UserProtocol until you declare it as such.
If for some reason you need to do some transformation of the Apollo types to fit the app models in the future, those extensions are a good place to do that, too.

How to store relational (one to many or many to one) data with Amplify iOS (AppSync)?

today checking some of the amplify documentation (I know this one says it is a preview in the iOS scenario) but I have ran into a road block.
Assumptions
Amplify is correctly configured in my iOS project. I can push data to Person and query the Amplify.API
The schema has been defined as:
type Person #model {
id: ID!
name: String!
possessions: [Thing] # list of things this person owns.
#connection(keyName: "byPerson", fields: ["id"])
}
type Thing #model
#key(name: "byPerson", fields: ["personId"]) {
id: ID!
name: String!
personId: ID!
ownerOfThings: Person # defining the 'belongsTo' property.
#connection(fields: ["personId"])
}
This generates the following code:
public struct Person: Model {
public let id: String
public var name: String
public var possessions: List<Thing>?
public init(id: String = UUID().uuidString,
name: String,
possessions: List<Thing>? = []) {
self.id = id
self.name = name
self.possessions = possessions
}
}
public struct Person: Model {
public let id: String
public var name: String
public var ownerOfThings: Person?
public init(id: String = UUID().uuidString,
name: String,
ownerOfThings: Person? = nil) {
self.id = id
self.name = name
self.ownerOfThings = ownerOfThings
}
}
Here is where I ran into trouble. Amplify.API doesn't seem be saving my object and its associated data in a single mutation. I have to call it as nested operations to have an effect.
// sample on how I am trying to save data.
var thing = Thing(name: "Long Claw")
let person = Person(
name: "Jon Snow",
possessions: List([ thing ])
)
Amplify.API.mutate(of: person, type: .create) { ev in
// doing something with the event.
print(String(describing: ev)) // this works. It saves the instance to DynamoDB
// unfortunately, it did not save the instance of thing... let's try to correct this.
thing.ownerOfThings = person
Amplify.API.mutate(of: thing, type: .create) { ev2 in
// do something else with this...
print(String(describing: ev2))
// this ^ crashes badly...
}
}
The code above will generate an output similar to:
Result.success(Person(id: "EC4BEEE1-C1A1-4831-AB86-EA1E22D8AD48", name: "Jon Snow", possessions: nil))
GraphQLResponseError<Thing>: GraphQL service returned a successful response containing errors: [Amplify.GraphQLError(message: "Variable \'input\' has coerced Null value for NonNull type \'ID!\'", locations: Optional([Amplify.GraphQLError.Location(line: 1, column: 26)]), path: nil, extensions: nil)]
I've tried declaring the relationship as:
type Person #model {
id: ID!
name: String!
possessions: [Thing] # list of things this person owns.
#connection(keyName: "byPerson", fields: ["id"])
}
type Thing #model
#key(name: "byPerson", fields: ["personId"]) {
id: ID!
name: String!
personId: ID!
# ownerOfThings: Person
# #connection(fields: ["personId"]) # Not belongsTo for you!
}
Or a variation of this, defining the possessions as possessions: [Thing] #connection.
All of them generate various (although some what related) errors, preventing me from storing my data.
So, the question is:
How do you specify the relationship in iOS to save it?

Subclassing PFUser does not effect the data stored

I am using Swift.
This question talks about the Parse service.
I've read ways using both the #NSManaged and dynamic key-words, so I decided for my example to implement them both. The issue here is that in the User object of my data manager, I'm noticing that additional information is not being stored in the database. For my application I would like to store some additional information in the User table, such as their first and last name. Here's an example:
import Parse
public class User : PFUser {
#NSManaged public var firstName: String!
dynamic public var lastName: String!
override public class func initialize() {
struct Static {
static var onceToken : dispatch_once_t = 0
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
init(email: String, password: String) {
super.init();
self.username = email;
self.email = email;
self.password = password;
self.firstName = "MyFirstName";
self.lastName = "MyLastName";
}
}
Here's the code I'm using to initialize and send off the data:
#IBAction func register() {
let newUser = User(email: "myemail#provider.com", password: "my password");
newUser.signUpInBackgroundWithBlock { (success, error) -> Void in
if error == nil {
print("Success")
} else {
print(error);
}
}
}
Edit: Upon playing around in the dashboard it seems like fields that are added to the User table / document / whatever you want to call it in a schema less database are automatically removed. This would indicate that I would need to create another class (IE: UserInfo) to store all of the users information, such as first and last name. Is this correct? If so that seems a little odd, as it would increase the amount of requests to login to the application. (One for validation, one for retrieving information). Is this the correct way of handling this?
Adding properties to a subclass won't them automatically added to the PFUser instance. You will have to use the PFObject methods to set the properties and then save the PFUser object.
The Parse Documentation gives an example of setting an extra phone property on the PFUser instance before calling signup. This is how you can add the firstName and lastName properties to PFUser.

ActiveRecord like reflection in swift

I was wondering how to accomplish an active record like (not with core data, but with rest, but this part I don't think will be an issue).
The issue its that don't understand well the reflection in swift, and my goal its to have a base class called Collection like (just prototype not really working code here):
public class Collection
{
public var Id: String
public static func Name () -> String
{
// accomplished but not with static method
}
public static func Find () -> [ChildClass] // the child class how can I obtain dynamically?
{
// restful things
return [ChildClass, ChildClass, ...] // rest result maped from json
}
public static func FindById (id : String) {}
// also Save && Delete methods
}
This kind of things I accomplish in c# with WSD-Data exactly in this class
So the usage maybe will:
public class User : Collection
{
public var FirstName: String
public var LastName: String
}
or c# like (dunno if this can be done in swift)
public class User : Collection<User> // we pass user as reference class c# like
{
public var FirstName: String
public var LastName: String
}
I hear also other alternatives to get this done, because don't know so well swift and the way that things are done with it.

Search and get all parents that contains a child with value

class Client {
String name
static hasMany = [courses:Course]
}
class Course {
String name
static belongsTo = [client:Client]
}
I have this and I want to get all Clients that has a Course with name = "blabla"
I was trying to do : Clients.findWhere(Course.any { course -> course.name = "math" })
You can do this with criteria:
Client.withCriteria {
courses {
eq('name', 'math')
}
}
I believe that the following where query is equivalent to the above criteria:
Client.where { courses.name == 'math' }
or you may find you need another closure:
Client.where {
courses {
name == 'math'
}
}
but I rarely use where queries myself so I'm not 100% sure of that.
There are probably a lot of different syntactical expressions to achieve the same thing. I can say definitively that this works in my project though.
def ls = Client.list {
courses {
eq('name','math')
}
}

Resources