Migration looks ok but is failing with an error - fluent

I have Migrations creating my models in a MySQL DB and this is working.
Now, I want to seed the DB with some demo data, so I created a SeedDB Migration for the sole purpose of this (of course, this migration is running after creating all model migrations).
struct SeedDB: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
Setup.createDemoData(database: database)
...
}
}
I created a Setup model for doing setup stuff. At this moment, it is only creating the demo data, but may be used in the future for other things.
final class Setup {
static func createDemoData (database : Database){
let _ = Address.createDemoData (database: database).map{ address in
let _ = Site.createDemoData(address: address, database: database).map {site in
let _ = Appointment.createDemoData (site: site, database: database)
let _ = Company.createDemoData(site: site, database: database)
let _ = ....
}
}
As you can see, I have to nest some calls because a Site can not be made without having an Address. An Appointment, Company and others need to have a Site as parameter... (in those models that is defined using #Parent relations).
On the level on creating a Site from an address this works (as expected) perfect. However, if I want to create an Appointment (or Company) there is problem, even though the migration seems to be successful there is an error :
Would you like to continue? y/n> y Migration successful Fatal error:
Unexpectedly found nil while unwrapping an Optional value: file
NIO/CircularBuffer.swift, line 538
My models look like:
In each of my Models I have one of the following versions of these functions (depending on the needs) :
static func createDemoData (database: Database) -> EventLoopFuture<myModel> {
let newMyModel = myModel(param1: "some stuff", param2: "demo info")
return newMyModel.save(on: database).map{newMyModel}
}
or
static func createDemoData (site: Site database: Database) -> EventLoopFuture<myModel> {
let newMyModel = myModel(param1: "some stuff", param2: "demo info")
return newMyModel.save(on: database).map{newMyModel}
}
or similar...
FYI removing the .save(on: database) in the createDemoData in Appointment or Company makes the error not happening (but also not saving the instance to the DB)
Added model Appointment per request of 0xTim
static func createDemoData (site: Site, database: Database) -> [Appointment]{
var appointment: Appointment
var appointments : [Appointment] = []
let today = Date()
let day = TimeInterval(24.0 * 3600.0)
//Planned appointments
appointment = Appointment(siteID: site.id!, appointmentDate: (today+(3*day)), description: "Call James Bond")
let _ = appointment.save(on: database)
appointments.append(appointment)
....
As far as I know site.id! is not the culprit, since it is created in the Setup Model with the createDemoData for the appointment in its closure. (see code above).
Added createDemoData for Site class per request of 0xTim
static func createDemoData (address : Address, database: Database) -> EventLoopFuture<Site> {
let site = Site(name: "mySite", addressID: address.id!)
return site.save(on: database).map{site}
}
Again, address.id! should not be the problem as Site.createDemoData is in the closure of the address creating (so it should exist).

The issue you have is that you're not handling your futures. Every time you do
let _ = appointment.save(on: database)
Or similar you're telling the compiler to return immediately whether or not the save has returned or even succeeded. You need to handle the futures correctly with map/flatMap. It's the same with your migration
let _ = Address.createDemoData (database: database).map{ address in
let _ = Site.createDemoData(address: address, database: database).map {site in
let _ = Appointment.createDemoData (site: site, database: database)
let _ = Company.createDemoData(site: site, database: database)
let _ = ....
}
}
There's no guarantee (and it's quite likely) that you'll attempt to create the Appointment before the Address has completed etc

Related

How to Connect to MySQL database using Swift-Nio

I used node js to connect mysql server and it is working good. I used the API developed in node js and used in the local macOS app. The problem is querying the data from mysql database on workbench is fast but in node js API it is very delay. So, I've to connect mysql db directly from my macOS app.
I installed mysql from here: https://www.mysql.com/downloads/
Also I am using mysql work bench to test the connection and It is connecting without any problem. So, In system preference I've an mysql extensions to start and stop mysql server. In the extension I can see the root folder locate in /usr/local/mysql
I created new project and added swift package of mysql-nio from here
In that repository their API document link is broken https://api.vapor.codes/mysql-nio/master/MySQLNIO/ But I found the correct link https://api.vapor.codes/mysql-nio/main/MySQLNIO/MySQLConnection/
By using that link, I tried to connect with below code,
//https://github.com/vapor/mysql-nio
import Cocoa
import MySQLNIO
class ViewController: NSViewController {
private var group: EventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 4)
private var eventLoop: EventLoop {
return self.group.next()
}
override func viewDidLoad() {
super.viewDidLoad()
do{
let socketAddress = try SocketAddress(ipAddress: "127.0.0.1", port: 3306)
let logger = Logger(label: "MyLogger")
let connection = MySQLConnection.connect(to: socketAddress, username: "root", database: "MyDB", password: "3$#sdRt34#sdf$dja2", tlsConfiguration: nil, serverHostname: nil, logger: logger, on: eventLoop)
connection.whenComplete { result in
switch result{
case .success(let connection):
print("Success! Status closed: \(connection.isClosed)")
case .failure(let error):
DispatchQueue.main.async {
NSApp.presentError(error)
}
}
}
}catch{
NSApp.presentError(error)
}
}
}
Error:
The operation couldn’t be completed. (NIOCore.IOError error 1.)
I added App Transport Security Settings -> Allow Arbitrary Loads to YES in the xcode project info
I don't know How to resolve this issue. Can you please help me to resolve this issue? Thank you!
In the app sandbox, you'll need to enable "Outgoing connections (client)" and then it should work.
Unfortunately, because of a Swift bug the error printout of Swift errors isn't actually useful if you do localizedDescription. To see the actual error, you'd need to put something like a print(error) in your error handler.
See how in the screenshot below, the print(error) prints actually useful text like connect(descriptor:addr:size:): Operation not permitted (errno: 1) if I don't check the "Outgoing connections (client)"?

Can't Connect to MySQL Server through Xcode application even tho I am able to connect it via Terminal

I'm trying to create an application that fetches data from the MySQL server.
I installed MySQL from Homebrew and using the PerfectMySql swift package.
I want to test it on my localhost first but I'm unable to connect it from my application.
When I mysql -u root -p in the command line, it works as it should.
My code for MySQL connection:
import PerfectHTTP
import PerfectHTTPServer
import PerfectMySQL
import Foundation
public class DB {
let host = "localhost" // or "127.0.0.1"
let user = "root"
let password = "12345678"
let database = "pets"
func databaseConnect(host: String, user: String, password: String, db: String) -> MySQL {
let mysql = MySQL() // Create an instance of MySQL to work with
let connected = mysql.connect(host: host, user: user, password: password, db: db)
guard connected else {
// verify that we have connected successfully
print(mysql.errorMessage())
return mysql
}
return mysql
}
public func insertGame(title: String, description: String, createdDate: String){
// Connect to our database
var db = databaseConnect(host: host, user: user, password: password, db: database)
defer {
db.close() //This defer block makes sure we terminate the connection once finished, regardless of the result
}
// Create the statement we wish to execute
let statement = MySQLStmt(db)
let insert = "INSERT INTO game(id, title, description, release_date) VALUES (\(statement.insertId()), '\(title)', '\(description)', '\(createdDate)');"
_ = statement.prepare(statement: insert)
// Run the query
let querySuccess = statement.execute()
// Check that our query was successfuly, otherwise return out
guard querySuccess else {
print(db.errorMessage())
return
}
print("Insert successful!");
}
}
When I start my application in Xcode, it gives me this error:
host = "127.0.0.1"
host = "localhost"
I searched the web and StackOverflow for hours but couldn't find anything to fix this issue.
Every other question asked here or other forums are about fixing connection problems via Terminal, which is not the issue for me here.
Only a related question asked here but it is unfortunately unanswered. -> Can't connect to MySQL server on 'localhost'
You said that you were receiving the following error on your macOS client.
Can't connect to MySQL server on '127.0.0.1' (1)
I know you've probably figured it out by now, but for the sake of future readers, this message (which returns an error code of 2003) can result on macOS targets if you neglect to enable “Outgoing Connections (Client)”:

Firebase cloud functions / One works, other (same function) not works

I am very nervous.. I can't test normally firebase cloud functions, because a lot of things don't work. I tested it, I copied same function with a different name, the new function don't work.
Why????
Why working helloworld and why not working tryhello???
cloud functions nodejs index.js:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.tryHello = functions.https.onCall((data, context) => {
let dataexample = {
name: 'examplename',
state: 'examplestate',
country: 'examplecountry'
};
let setDoc = db.collection('newexample').doc(data.text).set(dataexample);
return { text : "success. uid:" + context.auth.uid }
});
exports.helloWorld = functions.https.onCall((data, context) => {
let dataexample = {
name: 'examplename',
state: 'examplestate',
country: 'examplecountry'
};
let setDoc = db.collection('newexample').doc(data.text).set(dataexample);
return { text : "success. uid:" + context.auth.uid }
});
Unity C#:
public void testbutton()
{
var data = new Dictionary<string, object>();
data["text"] = "example";
//I tested "tryHello" and helloWorld"
FirebaseFunctions.DefaultInstance.GetHttpsCallable("tryHello")
.CallAsync(data).ContinueWith((task) =>
{
if (task.IsFaulted)
{
// Handle the error...
print("error");
}
else if (task.IsCompleted)
{
IDictionary snapshot = (IDictionary)task.Result.Data;
print("Result: " + snapshot["text"]);
}
}
Result:
1. First, I write unity: GetHttpsCallable("helloWorld") and save.
I start game, login, then I click testbutton.
result: firebase console: success create example collection, example document, country:examplecountry, name:examplename, state:examplestate. Ok good.
unity log:
1. User signed in successfully: Jane Q. User (qQC3wEOU95eDFw8UuBjb0O1o20G2)
UnityEngine.Debug:LogFormat(String, Object[])
loginfire:b__10_0(Task1) (at Assets/loginfire.cs:155)
2. Result: success. uid:qQC3wEOU95eDFw8UuBjb0O1o20G2
UnityEngine.MonoBehaviour:print(Object)
<>c:<testbutton>b__16_0(Task1) (at Assets/loginfire.cs:411)
System.Threading._ThreadPoolWaitCallback:PerformWaitCallback()
cloud functions "helloWorld" log:
Function execution started
Function execution took 3020 ms, finished with status code: 200
Ok. I delete "example" collection in firebase console.
2. Second, I write unity: GetHttpsCallable("tryHello") and save.
I start game, login, then I click testbutton.
result: not create collection.
unity log:
*1. User signed in successfully: Jane Q. User (qQC3wEOU95eDFw8UuBjb0O1o20G2)
UnityEngine.Debug:LogFormat(String, Object[])
loginfire:b__10_0(Task`1) (at Assets/loginfire.cs:155)
System.Threading._ThreadPoolWaitCallback:PerformWaitCallback()
error
UnityEngine.MonoBehaviour:print(Object)
<>c:b__16_0(Task`1) (at Assets/loginfire.cs:396)
System.Threading._ThreadPoolWaitCallback:PerformWaitCallback()*
cloud functions "tryHello" log:
nothing...
Why?
I don't understand. same, only the name is different!
And.. in many cases it show success but still does not update the database. or just much later. why? Lately, "helloWorld" also often writes an error, if I don't press the testbutton immediately after logging in, it can't read the uid.
I start to get tired of the system right from the start.
Thanks..
Solved!
I needed it configured to cloud console "tryHello" permission settings. (Not same helloworld settings.)
Lately, "helloWorld" also often writes an error, if I don't press the testbutton immediately after logging in, it can't read the uid.
-> I needed declared Firebasauth within testbutton().
Sorry for the question, thanks.

RxSwift observable error stops chain - Web services with Rx, how to recover?

obviously I am new to RxSwift and though I consumed a lot of documentations and speeches, I think I am missing some fundamental concepts.
In my app I have a RESTful web service to load various resources but the base url of the web service is unknown at build/start time. Instead I have a "URL resolver" web service which I can call with my apps bundle, version and possible environment ("production", "debug" or any custom string entered in the apps debug settings) to obtain the base url I then use for the actual service.
My thinking was that I would create 2 services, one for the URL resolver and one for the actual web service which gives me my resources. The URL resolver would have a Variable and a Observable. I use the variable to signal the need to refresh the base url via a web service call to the URL resolver. I do this by observing the variable and filter only for true values. A function in the service class set the variables value to true (initially it is false) and inside an observer of the filtered variable, I make the web service call in another Observable (this example uses a dummy JSON web service):
import Foundation
import RxSwift
import Alamofire
struct BaseURL: Codable {
let title: String
}
struct URLService {
private static var counter = 0
private static let urlVariable: Variable<Bool> = Variable(false)
static let urlObservable: Observable<BaseURL> = urlVariable.asObservable()
.filter { counter += 1; return $0 }
.flatMap { _ in
return Observable.create { observer in
let url = counter < 5 ? "https://jsonplaceholder.typicode.com/posts" : ""
let requestReference = Alamofire.request(url).responseJSON { response in
do {
let items = try JSONDecoder().decode([BaseURL].self, from: response.data!)
observer.onNext(items[0])
} catch {
observer.onError(error)
}
}
return Disposables.create() {
requestReference.cancel()
}
}
}
static func getBaseUrl() {
urlVariable.value = true;
}
static func reset() {
counter = 0;
}
}
Now the problem is that sometimes it can happen that a web service call fails and I would need to show the error to the user so a retry can be made. I thought that the onError was useful for this but it seems to kills all the subscribers forever.
I could put the subscribing in its own function and inside the error handler of the Observer, I could show a alert and then call the subscribe function again like so:
func subscribe() {
URLService.urlObservable.subscribe(onNext: { (baseURL) in
let alert = UIAlertController(title: "Success in Web Service", message: "Base URL is \(baseURL.title)", preferredStyle: .alert)
let actionYes = UIAlertAction(title: "Try again!", style: .default, handler: { action in
URLService.getBaseUrl()
})
alert.addAction(actionYes)
DispatchQueue.main.async {
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alert, animated: true, completion: nil)
}
}, onError: { error in
let alert = UIAlertController(title: "Error in Web Service", message: "Something went wrong: \(error.localizedDescription)", preferredStyle: .alert)
let actionYes = UIAlertAction(title: "Yes", style: .default, handler: { action in
URLService.reset()
self.subscribe()
})
alert.addAction(actionYes)
DispatchQueue.main.async {
VesselService.reset()
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alert, animated: true, completion: nil)
}
}).disposed(by: disposeBag)
}
Then in my AppDelegate I would call
subscribe()
URLService.getBaseUrl()
The problem is that all other observers get killed on an error as well but since the the only other observer on the URLService.urlObservable is my other web service class, I guess I could implement the same style subscribe function in there as well.
I read that some people suggest to return a Result enum which has 2 cases: the actual result (.success(result: T)) or an error (.error(error: Error)).
So what is the better way of handling errors web service errors in Rx? I cant wrap my head around this problem and I'm trying for 2 days to understand it. Any ideas or suggestions?
Update
It just came to my mind that I could ignore errors from the web service calls completely and instead post any error to a global "error" variable which my app delegate could observe to show alerts. The "error" could reference the function which initially caused it so a retry could be made. I'm still confused and not sure what I should do. :/
Update 2
I think I might found a working solution. As I am still a beginner to Rx and RxSwift, I'm happy to take improvement suggestions. As I was writing the actual code, I splitted my call chain in two parts:
The part where I make the web service calls
The part where I click a button and process the result of the web service, whether it is an error or a success
In the part where I click the button and process the result, I use catchError and retry as suggested in the comments. The code looks like this:
let userObservable = URLService
.getBaseUrl(environment: UserDefaults.standard.environment) //Get base url from web service 1
.flatMap({ [unowned self] baseURL -> Observable<User> in
UserService.getUser(baseURL: baseURL,
email: self.usernameTextField.text!,
password: self.passwordTextField.text!) //Get user from web service 2 using the base url from webservice 1
})
signInButton
.rx
.tap
.throttle(0.5, scheduler: MainScheduler.instance)
.flatMap({ [unowned self] () -> Observable<()> in
Observable.create { observable in
let hud = MBProgressHUD.present(withTitle: "Signing in...");
self.hud = hud
observable.onNext(())
return Disposables.create {
hud?.dismiss()
}
}
})
.flatMap({ () -> Observable<User> in
return userObservable
})
.catchError({ [unowned self] error -> Observable<User> in
self.hud?.dismiss()
self.handleError(error)
return userObservable
})
.retry()
.subscribe(onNext: { [unowned self] (user) in
UserDefaults.standard.accessToken = user.accessToken
UserDefaults.standard.tokenType = user.tokenType
self.hud?.dismiss()
})
.disposed(by: disposeBag)
The trick was to move the call to the two web services out of the cain into their own variable so I can re-call it at any time. When I now return the "userObservable" and an error happens during the web service call, I can show the error in the catchError and return the same "userObservable" for the next retry.
At the moment this only properly handles errors when they occur in the web service call chain so I think I should make the button tap a driver.
Okay so for everyone who comes here, you probably have a lack of understanding or a misconception of how the Rx world is supposed to work. I still find it sometimes confusing but I found a way better solution than what I posted in my original question.
In Rx, a error "kills" or rather completes all observers in the chain and that is actually a good thing. If there are expected errors like API error in web service calls, you should either try to handle them where they occur or treat them like expected values.
For example, your observer could return a optional type and subscribers could filter for the existence of values. If an error in the API call occurs, return nil. Other "error handlers" could filter for nil values to display error messages to the user.
Also viable is to return a Result enum with two cases: .success(value: T) and .error(error: Error). You treat the error as a acceptable result and the observer is responsible for checking if it should display a error message or the success result value.
Yet another option, which surely is not the best as well but works it to simply nest the call which you expect to fail inside the subscriber of the call which must not be affected. In my case that is a button tap which causes a call to a web service.
The "Update 2" of my original post would become:
signInButton.rx.tap.throttle(0.5, scheduler: MainScheduler.instance)
.subscribe(onNext: { [unowned self] () in
log.debug("Trying to sign user in. Presenting HUD")
self.hud = MBProgressHUD.present(withTitle: "Signing in...");
self.viewModel.signIn()
.subscribe(onNext: { [unowned self] user in
log.debug("User signed in successfully. Dismissing HUD")
self.hud?.dismiss()
}, onError: { [unowned self] error in
log.error("Failed to sign user in. Dismissing HUD and presenting error: \(error)")
self.hud?.dismiss()
self.handleError(error)
}).disposed(by: self.disposeBag)
}).disposed(by: self.disposeBag)
The MVVM view model makes the calls to the web serivces like so:
func signIn() -> Observable<User> {
log.debug("HUD presented. Loading BaseURL to sign in User")
return URLService.getBaseUrl(environment: UserDefaults.standard.environment)
.flatMap { [unowned self] baseURL -> Observable<BaseURL> in
log.debug("BaseURL loaded. Checking if special env is used.")
if let specialEnv = baseURL.users[self.username.value] {
log.debug("Special env is used. Reloading BaseURL")
UserDefaults.standard.environment = specialEnv
return URLService.getBaseUrl(environment: specialEnv)
} else {
log.debug("Current env is used. Returning BaseURL")
return Observable.just(baseURL)
}
}
.flatMap { [unowned self] baseURL -> Observable<User> in
log.debug("BaseURL to use is: \(baseURL.url). Now signing in User.")
let getUser = UserService.getUser(baseURL: baseURL.url, email: self.username.value, password: self.password.value).share()
getUser.subscribe(onError: { error in
UserDefaults.standard.environment = nil
}).disposed(by: self.disposeBag)
return getUser
}
.map{ user in
UserDefaults.standard.accessToken = user.accessToken
UserDefaults.standard.tokenType = user.tokenType
return user
}
}
First I was thinking to only call the view models signIn() function when pressing the button but since there should be no UI code in the view model, I figured that presenting and dismissing the HUD is the responsibility of the ViewController.
I think this design is now pretty solid. The button observer never completes and can continue to send events forever. Earlier, if there was a second error, it might happen that the button observer died and my logs showed that the userObservable was executed twice, which must also not be happen.
I just wonder if there is a better way then nesting the subscribers.

Returning a reference does not live long enough

I've just started learning Rust, and have come from a mainly JavaScript background so I'm a bit stumped when it comes to the whole borrowing system and memory management.
I have the following code:
fn load(db: &MyPool, id: i32) -> &Account{
let accounts: Vec<Account> = db.prepare("SELECT id, balance, name FROM `accounts` WHERE `id`=?")
.and_then(|mut stmt| {
stmt.execute(&[&id]).map(|result| {
result.map(|x| x.unwrap()).map(|row| {
Account{
id: from_value(&row[0]),
balance: from_value(&row[1]),
name: from_value(&row[2])
}
}).collect()
})
}).unwrap();
&accounts[0]
}
And I've managed to fix all the errors the compiler throws out apart from
/main.rs:42:4: 42:12 error: 'accounts' does not live long enough
Is this the best way to get one result from the MySQL query, or have I been going at it completely wrong?
You don't want to return a reference to an account, but you want to pass ownership to the caller after retrieving from the db.
Thus, change the signature to:
fn load(db: &MyPool, id: i32) -> Account
Now the idea would be to return the object by value, not by reference:
accounts[0]
However doing so will fail with an error: cannot move out of indexed content. A better approach would be to avoid collecting in a vector altogether, and use Iterator::next(&self) to take the first element. This would look like:
fn load(db: &MyPool, id: i32) -> Account{
let account: Account = db.prepare("SELECT id, balance, name FROM `accounts` WHERE `id`=?")
.and_then(|mut stmt| {
stmt.execute(&[&id]).map(|result| {
result.map(|x| x.unwrap()).map(|row| {
Account{
id: from_value(&row[0]),
balance: from_value(&row[1]),
name: from_value(&row[2])
}
}).next().unwrap() // <- next() takes the first elt of the iterator
})
}).unwrap();
account // <- return by value, pass ownership to caller
}
(Untested as I couldn't reproduce your dev environment.)
Kind of unrelated, but it is worth noting that those multiple unwrap() calls render your function extremely brittle as any failure will crash your whole program with a panic. Fortunately the answer to this bad smell is easy: you want to return Option<Account> rather than Account. Then remove all calls to unwrap() and let the Option<Account> propagate throughout calls (your use of map() is good because it says "return None if you find None and return Some(f(a)) if you find Some(a)".)