# Quick start

Previous pages had a lot of information on endless response codes and exit codes.
Do you really need all this complexity right now? Well, maybe. Or maybe not. We can say that you will surely need some of it, it all depends on your use case and how error-prone and self-sustainable you want your system to be.

# Start small, aim high

We recommend starting with the least amount of code and complexity, and adding it when it becomes necessary. You will absolutely be able to use automation without catching any of the exit codes. If you are the only cluster user (so, you use your cluster exclusively for automation and nothing else) - then you won't see a good half of possible error codes.

You can start building your application without processing any errors, only printing them to console in case of a failure, and then add code to catch them later if they are frequent for you.

The same mindset may be applied for browser session steps. E.g. you can skip folder operations and stopping profiles at first, and add them only when cloud storage becomes relevant for you. Have a look at how short your starter might look like:

val apiUrl = "https://api.surfinite.com"
val apiKey = "MY_API_KEY"
val profileId = "my_profile_id"
val url = "$apiUrl/profiles/$profileId/start?use_lock=false&api_key=$apiKey"

val browserKey = runCatching {
    HttpClient(CIO) { expectSuccess = true }.use { client ->
        val responseBodyString = client.post(url).bodyAsText()
        Json.parseToJsonElement(responseBodyString)
            .jsonObject["browserKey"]!!
            .jsonPrimitive.content
    }
}.getOrElse {
    it.printStackTrace()
    return
}

val profileDir = File("profiledirs").combineSafe(profileId)

val options = ChromeOptions()
options.addArguments(
    "--user-data-dir=\"${profileDir.absolutePath}\"",
    "--profile-id=$profileId",
    "--browser-key=$browserKey"
)

options.setBinary("/home/myuser/.surfinite/browser/chrome")
System.setProperty("webdriver.chrome.driver", "/home/myuser/.surfinite/browser/chromedriver")

val driver = runCatching { ChromeDriver(options) }
    .getOrElse {
        it.printStackTrace()
        return
    }

driver["https://example.com"]

This code is 36 lines long is capable of starting browser. You will be able to test your Selenium logic.

# Add complexity as you go

After adding folder download and upload steps (and even processing exit code 4 of it), the code is only 84 lines long.

val apiUrl = "https://api.surfinite.com"
val apiKey = "MY_API_KEY"
val profileId = "my_profile_id"
val url = "$apiUrl/profiles/$profileId/start?use_lock=false&api_key=$apiKey"

val browserKey = runCatching {
    HttpClient(CIO) { expectSuccess = true }.use { client ->
        val responseBodyString = client.post(url).bodyAsText()
        Json.parseToJsonElement(responseBodyString)
            .jsonObject["browserKey"]!!
            .jsonPrimitive.content
    }
}.getOrElse {
    it.printStackTrace()
    return
}

val profileDir = File("profiledirs").combineSafe(profileId)

var process = ProcessBuilder(
    listOf(
        "/home/myuser/.surfinite/aemulari/aemulari",
        "create-folder",
        "--profile-id", profileId,
        "--path", profileDir.absolutePath,
        "--api-key", apiKey
    )
)
    .redirectOutput(ProcessBuilder.Redirect.INHERIT)
    .redirectError(ProcessBuilder.Redirect.INHERIT)
    .start()

process.waitFor()

var code = process.exitValue()
if (code != 0) {
    println("Download folder exited with code $code")
    return
}

val options = ChromeOptions()
options.addArguments(
    "--user-data-dir=\"${profileDir.absolutePath}\"",
    "--profile-id=$profileId",
    "--browser-key=$browserKey"
)

options.setBinary("/home/myuser/.surfinite/browser/chrome")
System.setProperty("webdriver.chrome.driver", "/home/myuser/.surfinite/browser/chromedriver")

val driver = runCatching { ChromeDriver(options) }
    .getOrElse {
        it.printStackTrace()
        return
    }

driver["https://example.com"]
// ...
driver.quit()

process = ProcessBuilder(
    listOf(
        "/home/myuser/.surfinite/aemulari/aemulari",
        "upload-folder",
        "--profile-id", profileId,
        "--path", profileDir.absolutePath,
        "--api-key", apiKey,
        "--delete"
    )
)
    .redirectOutput(ProcessBuilder.Redirect.INHERIT)
    .redirectError(ProcessBuilder.Redirect.INHERIT)
    .start()

process.waitFor()

code = process.exitValue()
if (code != 0) {
    if (code == 4) profileDir.deleteRecursively()
    else {
        println("Upload folder exited with code $code")
        return
    }
}

What we are trying to say is - don't be afraid of all these possible failures. They are features for you in the future, not requirements. You will have to spend some more time on getting things working, though when it comes to optimization - you will have full control over each step and plenty of flexibility, which means faster development for you!