Skip to content

Commit

Permalink
feat: create cpf endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
rodcoffani authored and MarlonHenq committed Nov 30, 2023
1 parent 1fefaa2 commit 0c51a55
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 0 deletions.
60 changes: 60 additions & 0 deletions pages/api/cpf/v1/[cpf].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import app from '@/app';

import BaseError from '@/errors/BaseError';
import InternalError from '@/errors/InternalError';
import BadRequestError from '@/errors/BadRequestError';

import { cpfIsValid, getCpfRegionAndUfs, formatCpf } from '@/services/cpf/cpf';

async function regionOfCpf(request, response, next) {
try {
const requestedCpf = request.query.cpf.replace(/[-.]/g, '');
const formatedCpf = formatCpf(requestedCpf);

if (!/^[0-9]+$/.test(requestedCpf)) {
throw new BadRequestError({
message: `CPF ${formatedCpf} inválido.`,
type: 'BadRequestError',
name: 'bad_request',
});
}

if (requestedCpf.length !== 11) {
throw new BadRequestError({
message: `CPF ${formatedCpf} inválido.`,
type: 'bad_request',
name: 'BadRequestError',
});
}

if (!cpfIsValid(requestedCpf)) {
response.status(200);
return response.json({
isValid: false,
});
}

const cpfRegionAndUfs = getCpfRegionAndUfs(requestedCpf);

response.status(200);
return response.json({
cpf: formatedCpf,
isValid: true,
rf: cpfRegionAndUfs.regionNumber,
ufs: cpfRegionAndUfs.ufs,
});
} catch (error) {
if (error instanceof BaseError) {
console.log(error);
return next(error);
}

console.log(error);
throw new InternalError({
message: 'Todos os serviços de CPF retornaram erro.',
type: 'service_error',
});
}
}

export default app().get(regionOfCpf);
86 changes: 86 additions & 0 deletions pages/docs/doc/cpf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
{
"tags": [
{
"name": "CPF",
"description": "Informações referentes a CPFs"
}
],

"paths": {
"/cpf/v1/{cpf}": {
"get": {
"tags": ["CPF"],
"summary": "Verifica o CPF e caso válido retorna sua região",
"description": "",
"parameters": [
{
"name": "CPF",
"description": "O CPF (Cadastro de Pessoas Físicas) é um sistema de códigos de registro de contribuintes mantido pela Receita Federal que representa unicamente uma pessoa física no Brasil.\n",
"in": "path",
"required": false,
"schema": {
"type": "string",
"format": "string"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CPFInfo"
}
}
}
},
"400": {
"description": "CPF em um padrão inválido",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorMessage"
},
"example": {
"name": "BadRequestError",
"message": "CPF 10723555072 inválido.",
"type": "bad_request"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"CPFInfo": {
"title": "CPF",
"required": ["cpf", "isValid", "rf", "ufs"],
"type": "object",
"properties": {
"cpf": {
"type": "string"
},
"isValid": {
"type": "boolean"
},
"rf": {
"type": "integer"
},
"ufs": {
"type": "object"
}
},
"example": {
"cpf": "10723555079",
"isValid": true,
"rf": 0,
"ufs": ["RS"]
}
}
}
}
}
1 change: 1 addition & 0 deletions pages/sitemap.xml/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const getDocs = () => {
'ISBN',
'CPTEC',
'PIX',
'CPF',
];
};

Expand Down
69 changes: 69 additions & 0 deletions services/cpf/cpf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
export const formatCpf = (cpf) => {
return cpf.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, '$1.$2.$3-$4');
};

export const cpfIsValid = (cpf) => {
const arr = cpf.split('');
if (arr.every((digit) => digit === arr[0])) return false;
let add = 0;
let rev = 0;
for (let i = 0; i < 9; i += 1) add += parseInt(cpf.charAt(i), 10) * (10 - i);
rev = 11 - (add % 11);
if (rev === 10 || rev === 11) rev = 0;
if (rev !== parseInt(cpf.charAt(9), 10)) return false;

add = 0;
for (let i = 0; i < 10; i += 1) add += parseInt(cpf.charAt(i), 10) * (11 - i);
rev = 11 - (add % 11);
if (rev === 10 || rev === 11) rev = 0;
if (rev !== parseInt(cpf.charAt(10), 10)) return false;
return true;
};

export const getCpfRegionAndUfs = (requestedCpf) => {
const cpfDigit = Number(requestedCpf[8]);

const regionsAndufs = [
{
regionNumber: 0,
ufs: ['RS'],
},
{
regionNumber: 1,
ufs: ['DF', 'GO', 'MT', 'MS', 'TO'],
},
{
regionNumber: 2,
ufs: ['AC', 'AP', 'AM', 'PA', 'RO', 'RR'],
},
{
regionNumber: 3,
ufs: ['CE', 'MA', 'PI'],
},
{
regionNumber: 4,
ufs: ['AL', 'PB', 'PE', 'RN'],
},
{
regionNumber: 5,
ufs: ['BA', 'SE'],
},
{
regionNumber: 6,
ufs: ['MG'],
},
{
regionNumber: 7,
ufs: ['ES', 'RJ'],
},
{
regionNumber: 8,
ufs: ['SP'],
},
{
regionNumber: 9,
ufs: ['PR', 'SC'],
},
];
return regionsAndufs[cpfDigit];
};
63 changes: 63 additions & 0 deletions tests/cpf-v1.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const axios = require('axios');

const requestUrl = `${global.SERVER_URL}/api/cpf/v1`;

describe('api/cpf/v1 (E2E)', () => {
test('Utilizando um CPF válido: 54083180013', async () => {
const response = await axios.get(`${requestUrl}/54083180013`);
const { data, status } = response;

expect(status).toEqual(200);
expect(data.cpf).toEqual('540.831.800-13');
expect(data.rf).toEqual(0);
expect(data.ufs).toEqual(['RS']);
expect(data.isValid).toEqual(true);
});

test('Utilizando um CPF válido com formatação: 540.831.800-13', async () => {
try {
await axios.get(`${requestUrl}/540.831.800-13`);
} catch (error) {
const { response } = error;
const { data, status } = response;

expect(status).toEqual(200);
expect(data.cpf).toEqual('540.831.800-13');
expect(data.rf).toEqual(0);
expect(data.ufs).toEqual(['RS']);
expect(data.isValid).toEqual(true);
}
});

test('Utilizando um CPF inválido (valor): 00000000000', async () => {
try {
await axios.get(`${requestUrl}/00000000000`);
} catch (error) {
const { response } = error;
const { data, status } = response;

expect(status).toEqual(400);
expect(data).toEqual({
message: 'CPF 000.000.000-00 inválido.',
name: 'BadRequestError',
type: 'bad_request',
});
}
});

test('Utilizando um CPF inválido (tamanho): 123', async () => {
try {
await axios.get(`${requestUrl}/123`);
} catch (error) {
const { response } = error;
const { data, status } = response;

expect(status).toEqual(400);
expect(data).toEqual({
message: 'CPF 123 inválido.',
name: 'BadRequestError',
type: 'bad_request',
});
}
});
});

0 comments on commit 0c51a55

Please sign in to comment.