-
Notifications
You must be signed in to change notification settings - Fork 258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is the project still maintained? #127
Comments
@thesubr00t Given that the community of people who use this library is still active I'd say it is. Why are you asking ? Do you need help with any specific issue ? |
This repo does not seem to be actively maintained at the moment. We had to fork the project in order to address several issues we came across during integration. We've have been making fixes as needed over at https://github.com/carolhealth/redux-auth. Fair warning: we may not continue to support our fork. We're identifying significant roadblocks integrating this package. Most of the changes and fixes are driven by our business needs and we do not necessarily have the resources act as the primary maintainer. |
I keep making these client libraries and the javascript ecosystem keeps changing out from under me. I've been using an API client in all of my active projects that works a little better with all the latest stuff. I'm going to integrate it into this project, and hopefully it will make this project a little easier to maintain. But I don't have an ETA. It will probably be at least a few weeks, maybe more. I apologize to anyone that this causes problems for. |
@lynndylanhurley would you mind mentioning which API client you recommend? |
I'm wondering if the project is safe to use as it is not maintained for over 2 years ? Will it work with latest React and Redux libraries ? |
It won't. I'm using react 15.4.0 to make it work. |
Any update on the future of this? Also, why isn't this app mentioned in the devise_token_auth list of apps? |
To anyone that need frontend functions to work with
import { API_PROTOCOL, API_DOMAIN, headers } from '../config/constants'
import { parseResponse, paramsToObject } from './parse'
const endpoints = {
signOutPath: "/auth/sign_out",
emailSignInPath: "/auth/sign_in",
emailRegistrationPath: "/auth",
accountUpdatePath: "/auth",
accountDeletePath: "/auth/cancel",
passwordResetPath: "/auth/password",
passwordUpdatePath: "/auth/password",
tokenValidationPath: "/auth/validate_token",
validateUserEmail: "/user_info/email_exists",
}
export function getCredentials (headers) {
const credentialHeaders = {
'access-token': headers.get('access-token'),
client: headers.get('client'),
uid: headers.get('uid'),
expiry: headers.get('expiry'),
}
// if a request to the API happens too close to another, the API will not send the
// new credential headers, but the last one still validates to a new request.
return Object.values(credentialHeaders).includes(null) ? null : credentialHeaders
}
export async function login (email, password) {
const response = await fetch(
`${API_PROTOCOL}://${API_DOMAIN}${endpoints.emailSignInPath}`, {
method: 'POST', headers,
body: JSON.stringify({email, password})
})
const parsedResponse = await parseResponse(response)
if (response.ok) {
const credentials = getCredentials(response.headers)
if (!credentials) {throw new Error('Missing credentials at login response.')}
writeCredentials(credentials)
const user = parsedResponse.data
return user
} else {
return Promise.reject(parsedResponse.errors)
}
}
export async function register (email, password, password_confirmation) {
const response = await fetch(`${API_PROTOCOL}://${API_DOMAIN}${endpoints.emailRegistrationPath}`, {
method: 'POST',
headers,
body: JSON.stringify({
email,
password,
password_confirmation,
confirm_success_url: `${API_PROTOCOL}://${window.location.hostname}`
})
})
const unconfirmedNewUser = await parseResponse(response)
return unconfirmedNewUser.data
}
export async function validateCredentials () {
const credentials = readCredentials()
if (!credentials) { return Promise.reject('No credentials saved locally') }
const response = await fetch(`${API_PROTOCOL}://${API_DOMAIN}${endpoints.tokenValidationPath}`, {
method: 'GET',
headers: {...headers, ...credentials},
})
if (response.ok) {
const newCredentials = getCredentials(response.headers)
// too many reloads makes the API return empty headers,
// but current credentials are still valid(sometimes, urgh).
if (newCredentials) {writeCredentials(newCredentials)}
// get user data from response
const { data } = await parseResponse(response)
return data
} else {
// deleteCredentials()
return Promise.reject('Invalid local token')
}
}
export function saveQueryCredentials (query) {
query = paramsToObject(query)
try {
const client = query.client_id
const { uid, expiry, token } = query
const credentials = {'access-token': token, client, uid, expiry}
writeCredentials(credentials)
return true
} catch (e) {
console.error('Error saving credentials: ', e)
return false
}
}
export async function logout () {
try {
const credentials = readCredentials()
await fetch(`${API_PROTOCOL}://${API_DOMAIN}${endpoints.signOutPath}`, {
method: 'DELETE',
headers: {...headers, ...credentials},
})
deleteCredentials()
} catch (e) {
return Promise.reject('Error requesting logout: ', e)
}
}
export async function validateUserEmail (email) {
const response = await fetch(
`${API_PROTOCOL}://${API_DOMAIN}${endpoints.validateUserEmail}`, {
method: 'POST',
headers,
body: JSON.stringify({user_info: {email}})
})
return await parseResponse(response)
}
export function readCredentials () {
return JSON.parse(localStorage.getItem('default'))
}
export function writeCredentials (credentials) {
localStorage.setItem('default', JSON.stringify(credentials))
}
export function deleteCredentials () {
localStorage.removeItem('default')
}
export const API_PROTOCOL = process.env.REACT_APP_PROTOCOL
export const API_DOMAIN = process.env.REACT_APP_DOMAIN
export const headers = {
'Accept': 'application/vnd.mycompany+json;version=1',
'Content-Type': 'application/json',
} Then, whenever you need to call the api, here's what you need:
import { API_PROTOCOL, API_DOMAIN, headers } from '../config/constants'
import { readCredentials, writeCredentials, getCredentials } from './auth'
import { Map, fromJS } from 'immutable'
export async function parseResponse (response, cb = () => {}) {
const json = await response.json()
if (json && response.status >= 200 && response.status < 300) {
return parseBodyToCamelCase(json)
} else {
cb()
return Promise.reject(json);
}
}
export async function callAPI (
endpoint = '/',
subdomain,
method = 'GET',
body,
customHeaders
) {
if (method.match(/POST|PUT|PATCH/) && typeof body === 'undefined') {
throw new Error(`missing body in ${method} method`)
}
const url = `${API_PROTOCOL}://${API_DOMAIN}${endpoint}`
const credentials = readCredentials()
if (credentials) {
let subdomainHeader = {}
if (subdomain) { subdomainHeader = {Subdomain: subdomain} }
const request = {...{method}, headers: {
...headers, ...customHeaders, ...credentials, ...subdomainHeader}
}
if (body && (typeof body !== 'undefined')) {
request.body = JSON.stringify(parseToSneakCase(body))
}
const response = await fetch(url, request)
const newCredentials = getCredentials(response.headers)
if (newCredentials) { writeCredentials(newCredentials) }
if (customHeaders && customHeaders['Content-Type'].match(/application\/pdf/)) {
return await response.text()
} else {
return response.status === 204 ? null : await parseResponse(response)
}
} else {
return Promise.reject('Cannot make API call. Missing credentials.')
}
}
export function parseToSneakCase (immutableObj) {
immutableObj = immutableObj instanceof Object ? fromJS(immutableObj) : immutableObj
const parsedObj = {}
immutableObj.map((value, key) => {
// recursive call ( won't catch Object values )
value = Map.isMap(value) ? parseToSneakCase(value) : value
const snakeKey = toSnakeCase(key)
return parsedObj[snakeKey] = value
})
return fromJS(parsedObj)
}
// In order to speak JS and Ruby lang, we keep switching from sneak to camel case
export function parseBodyToCamelCase (obj) {
if (obj instanceof Array) {
const objList = []
obj.forEach(objectItem => objList.push(parseToCamelCase(objectItem)))
return objList
} else {
return parseToCamelCase(obj)
}
}
export function parseToCamelCase (obj) {
const parsedObj = {}
Object.keys(obj).forEach((key) => {
// recursive call
obj[key] = obj[key] instanceof Object ? parseToCamelCase(obj[key]) : obj[key]
const camelKey = toCamelCase(key)
parsedObj[camelKey] = obj[key]
})
return parsedObj
}
// fooBar => foo_bar
export function toSnakeCase (string) {
return string.replace(/[A-Z]/g, (letter) => (`_${letter.toLowerCase()}`))
}
// foo_bar => fooBar
export function toCamelCase (string) {
return string.replace(/_[a-z]/g, (match) => (`${match.substring(1).toUpperCase()}`))
}
export function paramsToObject (params) {
params = params.substring(1)
try {
params = JSON.parse('{"'
+ decodeURIComponent(params)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g,'":"')
+ '"}')
} catch (e) {
return null
}
return params
}
export function handleCreateRole (newRole) {
return async function (dispatch, getState) {
const currentSubdomain = getState().user.get('currentSubdomain')
dispatch(creatingRole())
try {
const createdRole = await callAPI('/roles', currentSubdomain, 'POST', newRole)
dispatch(creatingRoleSuccess(createdRole))
return createdRole
} catch (e) {
dispatch(creatingRoleFailure(e))
return null
}
}
} have fun |
@betoharres thank you for that snippet! I have trouble to find out what's behind the "./parse" file? I am relatively new to React, so can you help me with that, please? |
@exocode I forgot to put the name of the file in the snippets. I just updated it with the name now.
and I needed to transform this into camelCase like this:
because that's how it JS is used to work with(also some lints will throw errors by using sneak_case variables). |
Hi,
Given the number of pull requests and issues unanswered, I wonder if the project is still maintained.
Any information?
The text was updated successfully, but these errors were encountered: