Giter VIP home page Giter VIP logo

ferno's Introduction

Ferno ๐Ÿ”ฅ

Ferno allows you to easily connect your Vapor project with your Firebase realtime database. It is built with the brand new Vapor 4. It gives you a nice and clean interface to interact with the Firebase Realtime REST API. It will automatically turn the response into your class/struct!

Prerequisites

You will need:

  • Vapor 4.8+

Installing

In your Package.swift file, add the line

.package(url: "https://github.com/vapor-community/ferno.git", from: "0.6.0")

Also make sure you add Ferno as a dependency

dependencies: [
  .product(name: "Ferno", package: "ferno")
]

Setup

Legacy setup

  1. Ferno uses an access token to read and write to your database. First we will need to get your service account information.

    • Log into the Firebase console
    • Click the settings gear next to Project Overview
    • Select Project settings
    • Select the SERVICE ACCOUNTS tab
    • Click the button at the bottom that says GENERATE NEW PRIVATE KEY
    • This will download a .json file. You will now have access to the email and private key. You will pass those into the initialize during the next step.
  2. Register Ferno as a Provider and import Ferno. This is usually done in configure.swift

FirebaseSDK setup

  1. Log into the Firebase console
  2. Click the settings gear next to Project Overview
  3. Select Project settings
  4. Select the SERVICE ACCOUNTS tab
  5. Select Firebase Admin SDK option
  6. Click the button Generate new private key to download the json file.

Ferno setup

You can use the content of json file information to fill out the FernoDefaultConfiguration or use the json file to preper the FernoServiceJsonConfiguration.

import Ferno

let fernoConfiguration = FernoDefaultConfiguration(
    basePath: "database-url", 
    email: "service-account-email", 
    privateKey: "private-key"
)
app.ferno.use(.default(fernoConfiguration))

If you prefer to use the Firebase ServiceAccount.json follow the example below.

import Ferno

// option 1
let fernoConfiguration = try FernoServiceJsonConfiguration(json: Data)

// option 2
let fernoConfiguration = try FernoServiceJsonConfiguration(path: URL)

// option 3
let fernoConfiguration = FernoServiceJsonConfiguration(
                type: String,
                projectId: String,
                privateKeyId: String,
                privateKey: String,
                clientEmail: String,
                clientId: String,
                authUri: String,
                tokenUri: String,
                authProviderX509CertUrl: String,
                clientX509CertUrl: String,
                universeDomain: String
)

app.ferno.use(.serviceAccountKey(fernoConfiguration))

Parameters

There are some custom parameters to pass into functions. I want to go over all the parameters you will need to know.

[FernoQuery]

In GET requests, you might want to query on your data. This is what [FernoQuery] is for.

FernoQuery is an enum with:

  1. case shallow(Bool)
  2. case orderBy(FernoValue)
  3. case limitToFirst(FernoValue)
  4. case limitToLast(FernoValue)
  5. case startAt(FernoValue)
  6. case endAt(FernoValue)
  7. case equalTo(FernoValue)

These are all the possible queries that are allowed on Firebase according to the docs

NOTES on [FernoQuery]

  • shallow(Bool) cannot be mixed with any other query parameters.
  • you usually use orderBy(FernoValue) in conjunction with enums 3-7
  • using orderBy(FernoValue) alone will just order the data returned

FernoValue

You will notice most cases in FernoQuery have a value of FernoValue. FernoValue is just a wrapper for Bool, String, Int, Double, Float. So you can just do .startAt(5) and everything will work.

Examples of [FernoQuery]

Just using shallow:

[.shallow(true)]

Filter data to only return data that matches "age": 21:

[.orderBy("age"), .equalTo(21)]

Just orderBy(returns data in ascending order):

[.orderBy("age")]

Usage

There are 6 functions that allow you to interact with your Firebase realtime database.

GET

There are four functions that allow you get your data.

app.ferno.retrieve(_ path: [String], queryItems: [FernoQuery] = [])
app.ferno.retrieve(_ path: String..., queryItems: [FernoQuery] = [])
app.ferno.retrieveMany(_ path: [String], queryItems: [FernoQuery] = [])
app.ferno.retrieveMany(_ path: String..., queryItems: [FernoQuery] = [])

The only difference between retrieve and retrieveMany is the return type.

  • retrieve returns -> F where F is of type Decodable
  • retrieveMany returns -> [String: F] where F is of type Decodable and String is the key

Example

  1. Define the value you want the data converted.
struct Developer: Content {
   var name: String
   var favLanguage: String
   var age: Int
}
  1. Make the request. Make sure you set the type of the response so Ferno knows what to convert.
let developers: [String: Developer] = try await app.ferno.retrieveMany("developers")
let developer: Developer = try await app.ferno.retrieve(["developers", "dev1"])

POST

Used to create a new entry in your database

app.ferno.create(_ path: [String], body: T) try await -> FernoChild
app.ferno.create(_ path: String..., body: T) try await -> FernoChild
  • body: T is of type Content.
  • FernoChild is a struct:
struct FernoChild: Content {
  var name: String
}
  • FernoChild is returned, because the API request returns the key from the newly created child.

Example

let newDeveloper = Developer(name: "Elon", favLanguage: "Python", age: 46) // conforms to Content
let newDeveloperKey: FernoChild = try await app.ferno.create("developers", body: newDeveloper)

DELETE

Used to delete an entry in your database

app.ferno.delete(_ path: [String]) try await -> Bool
app.ferno.delete(_ path: String...) try await -> Bool
  • the delete method will return a boolean depending on if the delete was successful

Example

let successfulDelete: Bool = try await app.ferno.delete(["developers", "dev-1"])

PATCH

Update values at a specific location, but omitted values won't get removed

app.ferno.update(_ path: [String], body: T) try await -> T
app.ferno.update(_ path: String..., body: T) try await -> T
  • the update method will return the body

Example

struct UpdateDeveloperName: Content {
  var name: String
}

let newDeveloperName = UpdateDeveloperName(name: "Kimbal") //conforms to Content
let updatedDeveloperName: UpdateDeveloperName = try await app.ferno.update(["developers", newDeveloperKey.name], body: newDeveloper) //newDeveloperKey.name comes from the create method

PUT

Overwrite the current location with data you are passing in

client.ferno.overwrite(_ path: [String], body: T) try await -> T
client.ferno.overwrite(_ path: String..., body: T) try await -> T

Example

struct LeadDeveloper: Content {
  var name: String
  var company: String
  var age: Int
}

let leadDeveloper = LeadDeveloper(name: "Ashley", company: "Bio-Fit", age: 20)
let leadDevResponse: LeadDeveloper = try await app.ferno.overwrite(["developers", newDeveloperKey.name], body: leadDeveloper)

Testing

Currently, tests were written using an actual dummy Firebase realtime database. If you want to run all of the tests, you will need to create a dummy Firebase realtime database.

Testing Setup

You need to go to Application+Testing.swift and fill in the missing values based on your Firebase service account. Then you will be able to run tests.

Authors

License

This project is licensed under the MIT License - see the LICENSE.md file for details

Acknowledgments

  • Vapor Discord (for helping me with all my issues <3)
  • Stripe Provider as a great template! stripe-provider

ferno's People

Contributors

0xtim avatar aaastorga avatar chibombo avatar martinlasek avatar maximkrouk avatar petrpavlik avatar simonmitchell avatar vzsg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ferno's Issues

Doesn't conform to JWT 3.0.0 release

If your project depends on JWT 3.0.0 and you also try using this (Ferno) package with it's dependency of JWT being 3.0.0-rc.2.1.1, you'll end up having the highest thus JWT 3.0.0 fetched into your project.

This leads to Ferno not being able to compile because Payload no longer conforms to JWTPayload.

Currently it implements

struct Payload: JWTPayload {
  ...

  func verify() throws {
    try exp.verify()
  }
}

I haven't worked enough with JWT to create a mergable PR so I am not sure if this would even be a temporary fix for the current package implementation:

struct Payload: JWTPayload {
  ...

  func verify(using signer: JWTSigner) throws {
    try exp.verifyNotExpired()  // .verify() is deprecated
  }
}

But it conforms and compiles, obviously the signer passed into the function is never used, but this sounds like it does what it is expected to do: verify.

Value required for key 'access_token'

While trying to setup my app to get some data from Firebase, I get this strange error: [ ERROR ] DecodingError.keyNotFound: Value required for key 'access_token'. (ErrorMiddleware.swift:26)

Auth

Couldn't find any authentication example... Does it mean that all my rules in Firebase should allow access without authentication? Any plans on building user auth part as well?

404 Not Found

I try to implement the ferno but I got the below error. Kindly advice.

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.