FreeDatas2HTML/src/freeDatas2HTMLParserForJSON.ts

154 lines
5.9 KiB
TypeScript

const errors = require("./errors.js");
import { ParseErrors, ParseResults, Parsers, RemoteSource } from "./freeDatas2HTMLInterfaces";
export class ParserForJSON implements Parsers
{
private _datasRemoteSource: RemoteSource={ url:"" };
private _datas2Parse: string="";
private _parseResults: ParseResults|undefined=undefined;
// Revoir tous les protocoles possibles pour une source distante (http(s)://.. , ftp..)
set datasRemoteSource(source: RemoteSource)
{
if(source.url.trim().length === 0)
throw new Error(errors.parserNeedUrl);
else
{
source.url=source.url.trim();
this._datasRemoteSource=source;
}
}
get datasRemoteSource() : RemoteSource
{
return this._datasRemoteSource;
}
set datas2Parse(datas: string)
{
if(datas.trim().length === 0)
throw new Error(errors.parserNeedDatas);
else
this._datas2Parse=datas.trim();
}
get datas2Parse() : string
{
return this._datas2Parse;
}
get parseResults() : ParseResults|undefined
{
return this._parseResults;
}
// Refuse les champs qui ne sont pas des chaînes de caractères
// trim() les autres
public static trimAllFields(fields: any[]) : string[]
{
const nb=fields.length, goodFields: string[]=[];
for(let i=0; i < nb; i++)
{
if(typeof fields[i] === "string")
goodFields.push(fields[i].trim());
}
return goodFields;
}
// async dans le cas d'une source distante
public async parse(): Promise<any>
{
const parser=this;
let parseContent="";
if(parser._datasRemoteSource.url !== "")
{
const headers=new Headers();
if(parser._datasRemoteSource.headers !== undefined)
{
for(let header of parser._datasRemoteSource.headers)
headers.append(header.key, header.value);
}
const credentials : RequestCredentials|undefined=(parser._datasRemoteSource.withCredentials) ? "include" : "omit";
const settings={
method: "GET",
headers: headers,
credentials: credentials,
};
const response=await fetch(parser._datasRemoteSource.url, settings);
if (! response.ok)
throw new Error(errors.parserRemoteFail);
parseContent=await response.text(); // doit en fait retourner du JSON, mais il est parsé plus loin.
}
else if(parser._datas2Parse !== "")
parseContent=parser._datas2Parse;
else
throw new Error(errors.parserNeedSource);
try
{
const datasParsed=JSON.parse(parseContent);
const typesOkForValue=["boolean","number","string"];
let fields: string[]=[], datas: {[index: string]:string}[]=[], parseErrors: ParseErrors[]=[];
// Je peux recevoir 2 tableaux contenant respectivement la liste de champs : string[] + celle des données : any[][]
if(datasParsed.fields !== undefined && Array.isArray(datasParsed.fields) && datasParsed.datas !== undefined && Array.isArray(datasParsed.datas))
{
fields=ParserForJSON.trimAllFields(datasParsed.fields);
const nbFields=fields.length, nbDatas=datasParsed.datas.length;
for(let i=0; i < nbDatas; i++)
{
const dataObject: {[index: string]: string} = {}, nbObjFields=datasParsed.datas[i].length;
for(let j=0; j < nbObjFields && j < nbFields; j++)
{
if(typesOkForValue.indexOf(typeof datasParsed.datas[i][j]) === -1)
parseErrors.push({ row:i, message:errors.parserTypeError+typeof datasParsed.datas[i][j]});
else
dataObject[fields[j]]=datasParsed.datas[i][j]+""; // force le type String
}
if(Object.keys(dataObject).length !== 0)
datas.push(dataObject);
}
}
else // Ou un tableau d'objets {}[], dont les attributs sont les noms des champs
{
let i=0;
for(let data of datasParsed)
{
// Ici les champs sont découverts au fur et à mesure,
// leur ordre peut être différent d'une ligne à l'autre
// et tous les champs ne sont pas systématiquement présents
let dataObject: {[index: string]: string} = {}
for(let field in data)
{
if(typesOkForValue.indexOf(typeof data[field]) !== -1)
{
field=field.trim();
if(field !== "" && fields.indexOf(field) === -1)
fields.push(field);
dataObject[field]=data[field]+""; // force le type String
}
else
parseErrors.push({ row:i, message:errors.parserTypeError+typeof data[field]});
}
if(Object.keys(dataObject).length !== 0)
datas.push(dataObject);
i++;
}
}
if(fields.length === 0) // possible si données fournies non correctement formées.
throw new Error(errors.parserFail);
// datas et errors peuvent par contre rester vides.
parser._parseResults =
{
datas: datas,
errors: parseErrors,
fields: fields,
};
}
catch(e)
{
console.error(e);
throw new Error(errors.parserFail);
}
}
}