old servers uses numbers instead of digits in the API. This patch intercept API requests to make it compatible with new and old servers.pull/763/head
@@ -22,6 +22,17 @@ function removePasswordProfile(profile) { | |||
}; | |||
} | |||
function replaceNumbersWithDigitsInProfile(profile) { | |||
if ("numbers" in profile) { | |||
const { numbers, ...profileWithoutNumbers } = profile; | |||
return { | |||
...profileWithoutNumbers, | |||
digits: numbers, | |||
}; | |||
} | |||
return profile; | |||
} | |||
export function getPasswordProfiles() { | |||
return (dispatch, getState) => { | |||
const { settings, auth } = getState(); | |||
@@ -30,21 +41,39 @@ export function getPasswordProfiles() { | |||
headers: { Authorization: `Bearer ${auth.accessToken}` }, | |||
}) | |||
.then((response) => { | |||
dispatch(setPasswordProfiles(response.data.results)); | |||
const profiles = response.data.results.map( | |||
replaceNumbersWithDigitsInProfile | |||
); | |||
dispatch(setPasswordProfiles(profiles)); | |||
return response; | |||
}); | |||
}; | |||
} | |||
export function addNumbersFieldInProfile(profile) { | |||
return { | |||
...profile, | |||
numbers: profile.digits, | |||
}; | |||
} | |||
export function savePasswordProfile(profile) { | |||
return (dispatch, getState) => { | |||
const { settings, auth } = getState(); | |||
return axios | |||
.post(`${settings.baseURL}/passwords/`, profile, { | |||
headers: { Authorization: `Bearer ${auth.accessToken}` }, | |||
}) | |||
.post( | |||
`${settings.baseURL}/passwords/`, | |||
addNumbersFieldInProfile(profile), | |||
{ | |||
headers: { Authorization: `Bearer ${auth.accessToken}` }, | |||
} | |||
) | |||
.then((response) => { | |||
dispatch(addPasswordProfile({ ...response.data })); | |||
dispatch( | |||
addPasswordProfile( | |||
replaceNumbersWithDigitsInProfile({ ...response.data }) | |||
) | |||
); | |||
return response; | |||
}) | |||
.catch(() => | |||
@@ -61,11 +90,19 @@ export function updatePasswordProfile(profile) { | |||
return (dispatch, getState) => { | |||
const { settings, auth } = getState(); | |||
return axios | |||
.put(`${settings.baseURL}/passwords/${profile.id}/`, profile, { | |||
headers: { Authorization: `Bearer ${auth.accessToken}` }, | |||
}) | |||
.put( | |||
`${settings.baseURL}/passwords/${profile.id}/`, | |||
addNumbersFieldInProfile(profile), | |||
{ | |||
headers: { Authorization: `Bearer ${auth.accessToken}` }, | |||
} | |||
) | |||
.then((response) => { | |||
dispatch(addPasswordProfile({ ...response.data })); | |||
dispatch( | |||
addPasswordProfile( | |||
replaceNumbersWithDigitsInProfile({ ...response.data }) | |||
) | |||
); | |||
return response; | |||
}) | |||
.catch(() => | |||
@@ -0,0 +1,26 @@ | |||
import { | |||
replaceNumbersWithDigitsInProfile, | |||
addNumbersFieldInProfile, | |||
} from "./profilesActions"; | |||
it("replaceNumbersWithDigitsInProfile numbers become digits", () => { | |||
expect( | |||
replaceNumbersWithDigitsInProfile({ id: "p2", numbers: false }) | |||
).toEqual({ id: "p2", digits: false }); | |||
expect( | |||
replaceNumbersWithDigitsInProfile([{ id: "p1", numbers: true }]) | |||
).toEqual({ id: "p1", digits: true }); | |||
}); | |||
it("addNumbersFieldInProfile add numbers with digits", () => { | |||
expect(addNumbersFieldInProfile({ id: "p1", digits: true })).toEqual({ | |||
id: "p1", | |||
digits: true, | |||
numbers: true, | |||
}); | |||
expect(addNumbersFieldInProfile({ id: "p2", digits: false })).toEqual({ | |||
id: "p2", | |||
digits: false, | |||
numbers: false, | |||
}); | |||
}); |
@@ -3,23 +3,12 @@ const initialState = {}; | |||
export default function reduce(state = initialState, action) { | |||
switch (action.type) { | |||
case "SET_PASSWORD_PROFILES": | |||
return action.profiles | |||
.map((profile) => { | |||
if ("numbers" in profile) { | |||
const { numbers, ...profileWithoutNumbers } = profile; | |||
return { | |||
...profileWithoutNumbers, | |||
digits: numbers, | |||
}; | |||
} | |||
return profile; | |||
}) | |||
.reduce((acc, p) => { | |||
acc[p.id] = { | |||
...p, | |||
}; | |||
return acc; | |||
}, {}); | |||
return action.profiles.reduce((acc, profile) => { | |||
acc[profile.id] = { | |||
...profile, | |||
}; | |||
return acc; | |||
}, {}); | |||
case "ADD_PASSWORD_PROFILE": | |||
return { | |||
...state, | |||
@@ -18,23 +18,6 @@ describe("profiles reducer", () => { | |||
p2: { id: "p2" }, | |||
}); | |||
}); | |||
it("SET_PASSWORD_PROFILES numbers become digits", () => { | |||
expect( | |||
reducer( | |||
{}, | |||
{ | |||
type: "SET_PASSWORD_PROFILES", | |||
profiles: [ | |||
{ id: "p1", numbers: true }, | |||
{ id: "p2", numbers: false }, | |||
], | |||
} | |||
) | |||
).toEqual({ | |||
p1: { id: "p1", digits: true }, | |||
p2: { id: "p2", digits: false }, | |||
}); | |||
}); | |||
it("REMOVE_PASSWORD_PROFILE", () => { | |||
expect( | |||
reducer( | |||
@@ -11,7 +11,7 @@ | |||
"i18n:translate": "node src/i18n/translate.js", | |||
"prettier": "prettier --write 'src/**/*'", | |||
"test": "yarn test:unit && yarn test:e2e", | |||
"test:watch": "jest --watch", | |||
"test:watch": "vue-cli-service test:unit --watch", | |||
"test:unit": "vue-cli-service test:unit", | |||
"test:e2e": "vue-cli-service test:e2e --headless", | |||
"lint": "vue-cli-service lint" | |||
@@ -1,19 +1,55 @@ | |||
import http from "./http"; | |||
function addNumbersFieldInProfile(profile) { | |||
return { | |||
...profile, | |||
numbers: profile.digits, | |||
}; | |||
} | |||
function replaceNumbersWithDigitsInProfile(profile) { | |||
if ("numbers" in profile) { | |||
const { numbers, ...profileWithoutNumbers } = profile; | |||
return { | |||
...profileWithoutNumbers, | |||
digits: numbers, | |||
}; | |||
} | |||
return profile; | |||
} | |||
export default { | |||
all() { | |||
return http.get("/passwords/"); | |||
return http.get("/passwords/").then((response) => { | |||
response.data.results = response.data.results.map( | |||
replaceNumbersWithDigitsInProfile | |||
); | |||
return response; | |||
}); | |||
}, | |||
create(resource) { | |||
return http.post("/passwords/", resource); | |||
return http | |||
.post("/passwords/", addNumbersFieldInProfile(resource)) | |||
.then((response) => { | |||
response.data = replaceNumbersWithDigitsInProfile(response.data); | |||
return response; | |||
}); | |||
}, | |||
read(resource) { | |||
return http.get(`/passwords/${resource.id}/`); | |||
return http.get(`/passwords/${resource.id}/`).then((response) => { | |||
response.data = replaceNumbersWithDigitsInProfile(response.data); | |||
return response; | |||
}); | |||
}, | |||
update(resource) { | |||
return http.put(`/passwords/${resource.id}/`, resource); | |||
return http | |||
.put(`/passwords/${resource.id}/`, addNumbersFieldInProfile(resource)) | |||
.then((response) => { | |||
response.data = replaceNumbersWithDigitsInProfile(response.data); | |||
return response; | |||
}); | |||
}, | |||
delete(resource) { | |||
return http.delete(`/passwords/${resource.id}/`); | |||
} | |||
}, | |||
}; |
@@ -3,9 +3,11 @@ import defaultPasswordProfile from "../store/defaultPassword"; | |||
export function encryptPassword(email, password) { | |||
return LessPass.generatePassword( | |||
"lesspass.com", | |||
email, | |||
password, | |||
defaultPasswordProfile | |||
{ | |||
...defaultPasswordProfile, | |||
site: "lesspass.com", | |||
login: email, | |||
}, | |||
password | |||
); | |||
} |
@@ -4,51 +4,159 @@ import Passwords from "@/api/password"; | |||
const mock = new MockAdapter(axios); | |||
test("Passwords.create", () => { | |||
const password = { login: "text@example.org" }; | |||
test("Passwords.create on old server replace numbers field after creation", () => { | |||
mock | |||
.onPost("https://api.lesspass.com/passwords/", password) | |||
.reply(201, { ...password, id: "1" }); | |||
return Passwords.create(password).then(response => { | |||
const passwordCreated = response.data; | |||
expect(passwordCreated.id).toBe("1"); | |||
expect(passwordCreated.login).toBe(password.login); | |||
.onPost("https://api.lesspass.com/passwords/", { | |||
login: "text@example.org", | |||
digits: false, | |||
numbers: false, | |||
}) | |||
.reply(201, { id: "p1", login: "text@example.org", numbers: false }); | |||
const password = { login: "text@example.org", digits: false }; | |||
return Passwords.create(password).then((response) => { | |||
expect(response.status).toBe(201); | |||
expect(response.data).toEqual({ | |||
id: "p1", | |||
login: "text@example.org", | |||
digits: false, | |||
}); | |||
}); | |||
}); | |||
test("Passwords.all", () => { | |||
mock.onGet("https://api.lesspass.com/passwords/").reply(200, {}); | |||
return Passwords.all().then(response => { | |||
test("Passwords.create on new server keeps digits", () => { | |||
mock | |||
.onPost("https://api.lesspass.com/passwords/", { | |||
login: "text@example.org", | |||
digits: false, | |||
numbers: false, | |||
}) | |||
.reply(201, { id: "p1", login: "text@example.org", digits: false }); | |||
const password = { login: "text@example.org", digits: false }; | |||
return Passwords.create(password).then((response) => { | |||
expect(response.status).toBe(201); | |||
expect(response.data).toEqual({ | |||
id: "p1", | |||
login: "text@example.org", | |||
digits: false, | |||
}); | |||
}); | |||
}); | |||
test("Passwords.all on old server transform numbers into digits", () => { | |||
mock.onGet("https://api.lesspass.com/passwords/").reply(200, { | |||
results: [{ id: "p1", numbers: false }], | |||
}); | |||
return Passwords.all().then((response) => { | |||
expect(response.status).toBe(200); | |||
expect(response.data).toEqual({ | |||
results: [{ id: "p1", digits: false }], | |||
}); | |||
}); | |||
}); | |||
test("Passwords.all on new server keeps digits", () => { | |||
mock.onGet("https://api.lesspass.com/passwords/").reply(200, { | |||
results: [{ id: "p2", digits: false }], | |||
}); | |||
return Passwords.all().then((response) => { | |||
expect(response.status).toBe(200); | |||
expect(response.data).toEqual({ | |||
results: [{ id: "p2", digits: false }], | |||
}); | |||
}); | |||
}); | |||
test("Passwords.get", () => { | |||
test("Passwords.get on old server replace numbers with digits", () => { | |||
mock | |||
.onGet( | |||
"https://api.lesspass.com/passwords/c8e4f983-8ffe-b705-4064-d3b7aa4a4782/" | |||
) | |||
.reply(200, {}); | |||
.reply(200, { id: "p1", numbers: false }); | |||
return Passwords.read({ id: "c8e4f983-8ffe-b705-4064-d3b7aa4a4782" }).then( | |||
response => { | |||
(response) => { | |||
expect(response.status).toBe(200); | |||
expect(response.data).toEqual({ id: "p1", digits: false }); | |||
} | |||
); | |||
}); | |||
test("Passwords.update", () => { | |||
test("Passwords.get on new server keeps digits", () => { | |||
mock | |||
.onGet( | |||
"https://api.lesspass.com/passwords/c8e4f983-8ffe-b705-4064-d3b7aa4a4782/" | |||
) | |||
.reply(200, { id: "p1", digits: false }); | |||
return Passwords.read({ id: "c8e4f983-8ffe-b705-4064-d3b7aa4a4782" }).then( | |||
(response) => { | |||
expect(response.status).toBe(200); | |||
expect(response.data).toEqual({ id: "p1", digits: false }); | |||
} | |||
); | |||
}); | |||
test("Passwords.update on old server replace numbers field after update", () => { | |||
mock | |||
.onPut( | |||
"https://api.lesspass.com/passwords/c8e4f983-4064-8ffe-b705-d3b7aa4a4782/", | |||
{ | |||
id: "c8e4f983-4064-8ffe-b705-d3b7aa4a4782", | |||
login: "test@example.org", | |||
digits: true, | |||
numbers: true, | |||
} | |||
) | |||
.reply(200, { | |||
id: "c8e4f983-4064-8ffe-b705-d3b7aa4a4782", | |||
login: "test@example.org", | |||
numbers: true, | |||
}); | |||
const password = { | |||
id: "c8e4f983-4064-8ffe-b705-d3b7aa4a4782", | |||
login: "test@example.org" | |||
login: "test@example.org", | |||
digits: true, | |||
}; | |||
return Passwords.update(password).then((response) => { | |||
expect(response.status).toBe(200); | |||
expect(response.data).toEqual({ | |||
id: "c8e4f983-4064-8ffe-b705-d3b7aa4a4782", | |||
login: "test@example.org", | |||
digits: true, | |||
}); | |||
}); | |||
}); | |||
test("Passwords.update on new keeps digits", () => { | |||
mock | |||
.onPut( | |||
"https://api.lesspass.com/passwords/c8e4f983-4064-8ffe-b705-d3b7aa4a4782/", | |||
password | |||
{ | |||
id: "c8e4f983-4064-8ffe-b705-d3b7aa4a4782", | |||
login: "test@example.org", | |||
digits: true, | |||
numbers: true, | |||
} | |||
) | |||
.reply(200, {}); | |||
return Passwords.update(password).then(response => { | |||
.reply(200, { | |||
id: "c8e4f983-4064-8ffe-b705-d3b7aa4a4782", | |||
login: "test@example.org", | |||
digits: true, | |||
}); | |||
const password = { | |||
id: "c8e4f983-4064-8ffe-b705-d3b7aa4a4782", | |||
login: "test@example.org", | |||
digits: true, | |||
}; | |||
return Passwords.update(password).then((response) => { | |||
expect(response.status).toBe(200); | |||
expect(response.data).toEqual({ | |||
id: "c8e4f983-4064-8ffe-b705-d3b7aa4a4782", | |||
login: "test@example.org", | |||
digits: true, | |||
}); | |||
}); | |||
}); | |||
@@ -59,7 +167,7 @@ test("Passwords.delete", () => { | |||
) | |||
.reply(204); | |||
return Passwords.delete({ id: "c8e4f983-8ffe-4064-b705-d3b7aa4a4782" }).then( | |||
response => { | |||
(response) => { | |||
expect(response.status).toBe(204); | |||
} | |||
); | |||