Schedulerly Client Quick Start guide
Installation
To install the latest version of schedulerly-client
yarn add @appsocially/schedulerly-client
Clients and authentication
This package includes 2 clients for admin
and user
access. Both will have almost the same API for managing slots. The differences are:
-
admin
will have full permission access to any user/calendar within the app, and requires a secretadmin
api key to be able to access the REST API. This client will also have special API to allow single user access by provide a custom login token for user. While this can be much more powerful with full access permission, it can be slow in some operation (latency of seconds). -
user
will requires a custom token generated by theadmin
client (once), and will have limited access. They can onlywrite
to their own slots and calendar, and will have limitedread
permission on other's slots and calendars. While have less permission, this client can be faster than using justadmin
client (latency of hundreds milliseconds).
For better security and performance reason, a flavor of mixing both these 2 clients often is the recommended way to implement.
Quick-start
Most application will requires at least the custom token end-point for user
client access, this will describe the minimal setup to be able to do just that.
Admin
Client
This sample is using firebase functions as endpoint.
- Step 1: Initialization The admin client needs to be initialized just once, and can be used any where.
-
appToken
: (Required) Secret app token for admin access. -
endPoint
: (Required) Select which stage the client should connect to. There're 3 endpoints:-
development:
https://asia-northeast1-schedulerly-dev.cloudfunctions.net/api
-
stage:
https://asia-northeast1-schedulerly-stage.cloudfunctions.net/api
-
production:
https://asia-northeast1-schedulerly-prod.cloudfunctions.net/api
-
development:
const schedulerly = require('@appsocially/schedulerly-client')
const functions = require('firebase-functions')
const endPoint = functions.config().schedulerly.baseurl
const appToken = functions.config().schedulerly.app_token
module.exports.schedulerlyService = schedulerly.admin.client({
appToken,
endPoint
})
- Step 2: Create user
Let says once an user registerd, a record of user data is added to
/USERS/{ownerId}
firestore path. We can use firestore trigger to also create an user on schedulerly.
const userPath = '/USERS/{ownerId}'
module.exports.onUserCreate = functions
.firestore
.document(userPath)
.onCreate(async (snap, context) => {
let result = await schedulerlyService.createUser({})
console.log('Created user', result)
const { userId } = result
// Save the userId for token generator
saveSchedulerlyUserId(context.params.ownerId, userId)
})
- Step 3: Custom token generator
This step will provide us with an endpoint so that the
user
client can retrieve a token for client-side operations.
// We have initialized this service in step 1
const { schedulerlyService } = require('../utils/schedulerly')
const functions = require('firebase-functions')
exports.generateSchedulerlyToken = functions
.region(ourRegion)
.https
.onCall((_, { auth }) => {
// We would expect that the current user has already logged in into the
// application, so that we know their identity already. We then can
// figure out this user's schedulerly ID
const schedulerlyUserId = getSchedulerlyUserId(auth.uid)
return schedulerlyService.generateToken({ userId: `${schedulerlyUserId}` })
})
User
Client
These examples are written with vuejs.
- Step 4: Initialization
import { SchedulerlyClient } from '@appsocially/schedulerly-client'
export const schedulerlyClient = new SchedulerlyClient({
// The stage parameter would be for choosing which stage the client is using.
// The `admin` and `user` clients must be using the same stage.
stage: process.env.VUE_APP_STAGE
})
If the user has already logged in into the application. This user can be authenticated with a custom token and will have permission to write
into their own calendar and slots. For example: hiring-bot's employers who want to pick which days is available so that applicant can pick the date for an interview. Continue to step 5.1.
Otherwise, if the user is not logged in, and simply need to view the available slots to apply, like the applicant in the example above, continue to step 5.2.
- Step 5.1: Authentication
The function schedulerlyClient.authenticate
will return a promise, it must be called and finish before any other API is called. The function will check if the user has already been authenticated, if no onTokenRequired
will be called and please call the endpoint created in step 3 and pass the result like below. If the user has already been authenticated, onTokenRequired
will not be called and the functions will end immediately. After login, there's no expiration time for the user's session, so don't forget to signout when the user signout of the appplication.
// Exported schedulerlyClient from step 3
import { schedulerlyClient } from '@/helpers/schedulerly'
export default {
methods: {
mounted: () => {
schedulerlyClient.authenticate({
onTokenRequired: () => {
// Call the endpoint created on step 2
return generateSchedulerlyToken()
.then(({ data }) => data.token)
}
}).then(() => {
// Do other stuffs with schedulerlyClient
})
}
}
}
- Step 5.2: Skip authentication
There's a limited number of operation that an anonymous user can access, which are viewing the slots and calendars of an employer or trainer. This part is specifcally used for applicant side, which can only pick which slots for the meeting. Any operation that required write
permission, must be implemented with admin
client on server.
// Exported schedulerlyClient from step 3
import { schedulerlyClient } from '@/helpers/schedulerly'
export default {
methods: {
skipAuthenticateSchedulerlyClient ({ ownerId }) {
schedulerlyClient.skipAuth({
userId: await getSchedulerlyUserId(ownerId)
})
},
mounted: () => {
this.skipAuthenticateSchedulerlyClient({ ownerId: __id of calendar owner__ })
}
}
}
- Step 5: Signout
When the user signout of the application, we must signout of schedulerly also.
schedulerlyClient.signOut()