mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
Rewrite Client Library
The following changes were made: - Modify index.js to mainly be the entry point for the client. - Write Base.js that has both RESTful and Realtime functionalities. - Delete Subscription.js as its functionality has already been merged into Base.js - Rewrite BaseRequest.js to accomodate new changes. - Write Filters.js to put filters into pure functions.
This commit is contained in:
1 parent
7d4c5397cb
commit
70f06dcd42
5 files changed
+237
-105
No files matched your search
@@ -0,0 +1,144 @@
|
||||
import { Socket } from 'phoenix-channels'
|
||||
import BaseRequest from './BaseRequest'
|
||||
|
||||
class Base {
|
||||
constructor(tableName, restUrl, apiSocket, uuid) {
|
||||
this.tableName = tableName
|
||||
this.restUrl = restUrl
|
||||
this.apiSocket = apiSocket
|
||||
this.uuid = uuid
|
||||
|
||||
this.socket = null
|
||||
this.channel = null
|
||||
this.listeners = {}
|
||||
|
||||
this.queryFilters = []
|
||||
}
|
||||
|
||||
createListener() {
|
||||
this.socket = new Socket(apiSocket)
|
||||
this.channel = this.socket.channel(this.tableName)
|
||||
}
|
||||
|
||||
on(eventType, callbackFunction) {
|
||||
if (this.socket == null) this.createListener()
|
||||
|
||||
var ref = this.channel.on(eventType, callbackFunction)
|
||||
this.listeners[eventType] = ref
|
||||
}
|
||||
|
||||
subscribe() {
|
||||
if (this.socket == null) this.createListener()
|
||||
|
||||
this.socket.connect()
|
||||
|
||||
if (this.channel.state !== 'joined') {
|
||||
this.channel
|
||||
.join()
|
||||
.receive('ok', resp => console.log('Joined Realtime successfully ', resp))
|
||||
.receive('error', resp => console.log('Unable to join ', resp))
|
||||
.receive('timeout', () => console.log('Networking issue. Still waiting...'))
|
||||
}
|
||||
}
|
||||
|
||||
unsubscribe() {
|
||||
for (var ref in this.listeners) {
|
||||
let eventName = this.listeners[ref]
|
||||
this.off(eventName, ref)
|
||||
}
|
||||
|
||||
this.socket.disconnect()
|
||||
}
|
||||
|
||||
request(method) {
|
||||
let path = `${this.restUrl}/${this.tableName}`
|
||||
return new BaseRequest(method, path)
|
||||
}
|
||||
|
||||
addFilters(request, options) {
|
||||
if (Object.keys(options).length != 0) {
|
||||
Object.keys(options).forEach(option => {
|
||||
let setting = options[option]
|
||||
request.set(option, setting)
|
||||
})
|
||||
}
|
||||
|
||||
// loop through this.queryFilters
|
||||
this.queryFilters.forEach(queryFilter => {
|
||||
|
||||
let columnName = queryFilter.columnName
|
||||
let operator = queryFilter.operator
|
||||
let criteria = queryFilter.criteria
|
||||
|
||||
request.filter(columnName, operator, criteria)
|
||||
})
|
||||
}
|
||||
|
||||
select(columnQuery = '*', options = {}) {
|
||||
let method = 'get'
|
||||
let request = this.request(method)
|
||||
|
||||
request.select(columnQuery)
|
||||
this.addFilters(request, options)
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
insert(data, options = {}) {
|
||||
let method = 'post'
|
||||
let request = this.request(method)
|
||||
|
||||
if (!Array.isArray(data)) {
|
||||
return {
|
||||
body: null,
|
||||
status: 400,
|
||||
statusCode: 400,
|
||||
statusText: 'Data type should be an array.',
|
||||
}
|
||||
}
|
||||
|
||||
data.forEach(datum => {
|
||||
request.send(datum)
|
||||
})
|
||||
|
||||
this.addFilters(request, options)
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
update(data, options = {}) {
|
||||
let method = 'patch'
|
||||
let request = this.request(method)
|
||||
|
||||
request.send(data)
|
||||
this.addFilters(request, options)
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
delete(options = {}) {
|
||||
let method = 'delete'
|
||||
let request = this.request(method)
|
||||
|
||||
this.addFilters(request, options)
|
||||
|
||||
return request
|
||||
}
|
||||
}
|
||||
|
||||
// pre-empts if any of the filters are used before select
|
||||
const filters = ['eq', 'gt', 'lt', 'gte', 'lte', 'like', 'ilike', 'is', 'in', 'not']
|
||||
|
||||
filters.forEach(
|
||||
operator =>
|
||||
(Base.prototype[operator] = function filterValue(columnName, criteria) {
|
||||
this.queryFilters.push({
|
||||
columnName,
|
||||
operator,
|
||||
criteria,
|
||||
})
|
||||
return this
|
||||
})
|
||||
)
|
||||
|
||||
export default Base
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { Request } from 'superagent'
|
||||
import * as Filters from './utils/Filters'
|
||||
|
||||
const contentRangeStructure = /^(\d+)-(\d+)\/(\d+)$/
|
||||
|
||||
@@ -21,6 +22,19 @@ class BaseRequest extends Request {
|
||||
super(method, path)
|
||||
this.set('Accept', 'application/json')
|
||||
|
||||
this.filterMap = {
|
||||
eq: (columnName, criteria) => Filters.Eq(columnName, criteria),
|
||||
gt: (columnName, criteria) => Filters.Gt(columnName, criteria),
|
||||
lt: (columnName, criteria) => Filters.Lt(columnName, criteria),
|
||||
gte: (columnName, criteria) => Filters.Gte(columnName, criteria),
|
||||
lte: (columnName, criteria) => Filters.Lte(columnName, criteria),
|
||||
like: (columnName, criteria) => Filters.Like(columnName, criteria),
|
||||
ilike: (columnName, criteria) => Filters.Ilike(columnName, criteria),
|
||||
is: (columnName, criteria) => Filters.Is(columnName, criteria),
|
||||
in: (columnName, criteria) => Filters.In(columnName, criteria),
|
||||
not: (columnName, criteria) => Filters.Not(columnName, criteria),
|
||||
}
|
||||
|
||||
// Fix for superagent disconnect on client & server.
|
||||
if (!this.get) {
|
||||
this.get = this.getHeader
|
||||
@@ -28,7 +42,7 @@ class BaseRequest extends Request {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set auth using special formats. If only one string paramter is passed, it
|
||||
* Set auth using special formats. If only one string parameter is passed, it
|
||||
* is interpreted as a bearer token. If an object and nothing else is passed,
|
||||
* `user` and `pass` keys are extracted from it and used for basic auth.
|
||||
*
|
||||
@@ -51,6 +65,20 @@ class BaseRequest extends Request {
|
||||
return super.auth(user, pass)
|
||||
}
|
||||
|
||||
/**
|
||||
* All in one conveniencd function for all filters available.
|
||||
*
|
||||
* @param {string} Name of the database column.
|
||||
* @param {string} Name of filter operator be utilised.
|
||||
* @param { object | array | string | integer | boolean | null } Value to compare to. Exact data type of criteria would depend on the operator used.
|
||||
* @returns {BaseRequest} The API request object.
|
||||
*/
|
||||
|
||||
filter(columnName, operator, criteria){
|
||||
let newQuery = this.filterMap[operator.toLowerCase()](columnName, criteria)
|
||||
return this.query(newQuery)
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a query object and translates it to a PostgREST filter query string.
|
||||
* All values are prefixed with `eq.`.
|
||||
@@ -179,15 +207,15 @@ class BaseRequest extends Request {
|
||||
* For all of the PostgREST filters add a shortcut method to use it.
|
||||
*
|
||||
* @param {string} The name of the column.
|
||||
* @param {any} The value of the column to be filtered.
|
||||
* @param { object | array | string | integer | boolean | null } The value of the column to be filtered.
|
||||
* @returns {BaseRequest} The API request object.
|
||||
*/
|
||||
|
||||
const filters = ['eq', 'gt', 'lt', 'gte', 'lte', 'like', 'ilike', 'is', 'in', 'not']
|
||||
|
||||
filters.forEach(filter =>
|
||||
BaseRequest.prototype[filter] = function filterValue (name, value) {
|
||||
return this.query(`${name}=${filter}.${Array.isArray(value) ? value.join(',') : value}`)
|
||||
BaseRequest.prototype[filter] = function filterValue (columnName, criteria) {
|
||||
return this.filter(columnName, filter, criteria)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import { Socket } from 'phoenix-channels'
|
||||
|
||||
export default class BaseChannel {
|
||||
constructor(tableName, apiSocket, uuid){
|
||||
this.socket = new Socket(apiSocket)
|
||||
this.uuid = uuid
|
||||
this.tableName = tableName
|
||||
this.channel = this.socket.channel(tableName)
|
||||
|
||||
this.start()
|
||||
|
||||
this.socket.onOpen(() => {
|
||||
console.log('Socket connected')
|
||||
})
|
||||
this.socket.onClose(() => {
|
||||
console.log('Socket disconnected')
|
||||
})
|
||||
}
|
||||
|
||||
on(eventName, callbackFunction){
|
||||
this.channel.on(eventName, callbackFunction)
|
||||
}
|
||||
|
||||
start(){
|
||||
this.socket.connect()
|
||||
|
||||
if(this.channel.state !== 'joined'){
|
||||
this.channel
|
||||
.join()
|
||||
.receive('ok', resp => console.log('Joined Realtime successfully ', resp))
|
||||
.receive('error', resp => console.log('Unable to join ', resp))
|
||||
.receive('timeout', () => console.log('Networking issue. Still waiting...'))
|
||||
}
|
||||
}
|
||||
|
||||
stop(){
|
||||
for (var ref in this.listeners){
|
||||
let eventName = this.listeners[ref]
|
||||
this.off(eventName, ref)
|
||||
}
|
||||
|
||||
this.socket.disconnect()
|
||||
}
|
||||
}
|
||||
@@ -1,82 +1,47 @@
|
||||
import BaseRequest from './BaseRequest'
|
||||
import Subscription from './Subscription'
|
||||
import { uuid } from './utils/Helpers'
|
||||
import Base from './Base'
|
||||
|
||||
class SupabaseClient {
|
||||
constructor(supabaseUrl, supabaseKey, options) {
|
||||
constructor(supabaseUrl, supabaseKey, options = {}) {
|
||||
this.supabaseUrl = supabaseUrl
|
||||
this.supabaseKey = supabaseKey
|
||||
|
||||
// this will be the case for now
|
||||
this.restUrl = supabaseUrl
|
||||
this.apiSocket = ''
|
||||
|
||||
this.restUrl = null
|
||||
this.apiSocket = null
|
||||
this.subscriptions = {}
|
||||
|
||||
this.authenticate(supabaseUrl, supabaseKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
subscribe(tableName) {
|
||||
let uuid = uuid()
|
||||
authenticate(supabaseUrl, supabaseKey){
|
||||
// do something to utilise parameters and receive
|
||||
// the restUrl & apiSocket
|
||||
|
||||
var restUrl = supabaseUrl
|
||||
var apiSocket = ''
|
||||
|
||||
this.subscriptions[uuid] = new Subscription(tableName, this.apiSocket, uuid)
|
||||
return this.subscriptions[uuid]
|
||||
this.restUrl = restUrl
|
||||
this.apiSocket = apiSocket
|
||||
}
|
||||
|
||||
unsubscribe(subscription){
|
||||
subscription.stop()
|
||||
delete this.subscriptions[subscription.uuid]
|
||||
from(tableName){
|
||||
let identifier = uuid()
|
||||
|
||||
this.subscriptions[identifier] = new Base(tableName, this.restUrl, this.apiSocket, identifier)
|
||||
return this.subscriptions[identifier]
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper for starting a PostgREST request builder. Adds the
|
||||
* API URL to the provided path.
|
||||
*
|
||||
* @param {string} The HTTP method of the request.
|
||||
* @param {string} The path of the request.
|
||||
* @returns {BaseRequest} The API request object.
|
||||
*/
|
||||
|
||||
request(method, path) {
|
||||
return new BaseRequest(method, this.restUrl + path)
|
||||
removeSubscription(mySubscription){
|
||||
mySubscription.unsubscribe()
|
||||
delete this.subscriptions[mySubscription.uuid]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic HTTP method functions for quick chaining.
|
||||
*
|
||||
* @param {string} The path of the request.
|
||||
* @returns {BaseRequest} The API request object.
|
||||
*/
|
||||
|
||||
const methods = ['POST', 'GET', 'PATCH', 'DELETE']
|
||||
|
||||
methods.forEach(method => {
|
||||
SupabaseClient.prototype[method.toLowerCase()] = function requestMethod(tableName) {
|
||||
let path = `/${tableName}`
|
||||
return this.request(method, path)
|
||||
}
|
||||
})
|
||||
|
||||
const createClient = (supabaseUrl, supabaseKey, options = {}) => {
|
||||
return new SupabaseClient(supabaseUrl, supabaseKey, options)
|
||||
}
|
||||
|
||||
export { createClient }
|
||||
|
||||
/**
|
||||
* TO BE REMOVED SOON
|
||||
*/
|
||||
|
||||
// const defaultAwesomeFunction = (name) => {
|
||||
// const returnStr = `I am the Default Awesome Function, fellow comrade! - ${name}`;
|
||||
// return returnStr;
|
||||
// };
|
||||
|
||||
// const awesomeFunction = () => 'I am just an Awesome Function';
|
||||
|
||||
// export default defaultAwesomeFunction;
|
||||
|
||||
// export { awesomeFunction };
|
||||
@@ -0,0 +1,39 @@
|
||||
export function Eq(columnName, filterValue){
|
||||
return `${columnName}=eq.${filterValue}`
|
||||
}
|
||||
|
||||
export function Gt(columnName, filterValue){
|
||||
return `${columnName}=gt.${filterValue}`
|
||||
}
|
||||
|
||||
export function Lt(columnName, filterValue){
|
||||
return `${columnName}=lt.${filterValue}`
|
||||
}
|
||||
|
||||
export function Gte(columnName, filterValue){
|
||||
return `${columnName}=gte.${filterValue}`
|
||||
}
|
||||
|
||||
export function Lte(columnName, filterValue){
|
||||
return `${columnName}=lte.${filterValue}`
|
||||
}
|
||||
|
||||
export function Like(columnName, stringPattern){
|
||||
return `${columnName}=like.${stringPattern}`
|
||||
}
|
||||
|
||||
export function Ilike(columnName, stringPattern){
|
||||
return `${columnName}=ilike.${stringPattern}`
|
||||
}
|
||||
|
||||
export function Is(columnName, filterValue){
|
||||
return `${columnName}=is.${filterValue}`
|
||||
}
|
||||
|
||||
export function In(columnName, filterArray){
|
||||
return `${columnName}=in.${filterArray.join(',')}`
|
||||
}
|
||||
|
||||
export function Not(columnName, filterValue){
|
||||
return `${columnName}=not.${filterValue}`
|
||||
}
|
||||
Reference in new issue
Block a user