gd-sprest-def
Version:
TypeScript definition files generated from the $metadata REST endpoint in SharePoint.
1,185 lines (1,021 loc) • 75.7 kB
JavaScript
let fs = require("fs");
let parser = require("xml2js").parseString;
let rmDir = require("rimraf");
let custom = require("./custom");
let customV2 = require("./customV2");
// Method to analyze the interfaces
function analyzeCollections(directories) {
let hasCollections = {};
let hasCollectionMethods = {};
let hasMethods = {};
// Parse the directories
for (let dirName in directories) {
// Parse the filenames
for (let filename in directories[dirName]) {
// Parse the interfaces
for (let name in directories[dirName][filename]) {
let interface = directories[dirName][filename][name];
// Set the flags
hasCollections[dirName + "." + name] = interface._Collections ? true : false;
hasCollectionMethods[dirName + "." + name] = interface._CollectionMethods ? true : false;
hasMethods[dirName + "." + name] = interface._Methods ? true : false;
}
}
}
// Return the data
return { hasCollections, hasCollectionMethods, hasMethods };
}
// Parse the methods and apply the methods to the directories object
function applyMethodsToDirectories(methods, directories) {
// Parse the custom methods
for (let name in custom) {
// Get the methods
let oldMethods = methods[name] || [];
// Parse the custom methods
for (let i = 0; i < custom[name].length; i++) {
let methodInfo = custom[name][i];
// Parse the old methods
for (let i = 0; i < oldMethods.length; i++) {
let oldMethod = oldMethods[i];
// See the method name exists
if (oldMethod.name == methodInfo.name) {
// Comment out the method
oldMethod.name = "// " + oldMethod.name;
}
}
}
// Add the methods
methods[name] = oldMethods.concat(custom[name]);
}
// Parse the methods
for (let name in methods) {
let isCollection = false;
let method = methods[name];
// See if this is a collection
if (/^Collection\(.*\)$/.test(name)) {
// Update the name
name = name.replace(/^Collection\(/, "").replace(/\)$/, "");
// Set the flags
isCollection = true;
}
// Find the last index of '.'
let idx = name.lastIndexOf('.');
// Set the lib and obj name
let lib = name.substring(0, idx);
let obj = name.substring(idx + 1);
// See if a file name exists for this object
if (directories[lib] && directories[lib].EntityTypes && directories[lib].EntityTypes[obj]) {
// Set the object
obj = directories[lib].EntityTypes[obj];
// See if this is a collection
if (isCollection) {
// Add the method
obj._CollectionMethods = (obj._CollectionMethods || []).concat(method);
} else {
// Add the method
obj._Methods = (obj._Methods || []).concat(method);
}
}
}
}
// Recursively create the directories and index files from the namespace
function createDirectories(path, namespace) {
// See if we need to create directories
if (namespace.indexOf('.') > 0) {
// Get the sub directories
namespace.split('.').forEach(function (dirName) {
// Update the path
path += "/" + dirName
// See if the directory doesn't exists
if (!fs.existsSync(path)) {
// Create the directory
fs.mkdirSync(path, function (err) { });
}
});
} else {
// Update the path
path += "/" + namespace
// See if the directory doesn't exist
if (!fs.existsSync(path)) {
// Create the directory
fs.mkdirSync(path, function (err) { });
}
}
// Return the path
return path;
};
// Creates the interface
function createInterface(name, baseType, variables) {
return [
'/*********************************************',
'* ' + name,
'**********************************************/',
'export interface ' + name + (baseType ? " extends " + baseType : "") + ' {',
variables,
'}',
''
].join('\n');
}
// Generates the base collection interface
function generateBaseCollection(methodType, hasCollections, hasCollectionMethods) {
// See if a collection exists
if (hasCollections[methodType]) {
// See if methods exist
if (hasCollectionMethods[methodType]) {
// Generate the interface
return 'Base.IBaseCollection<' + methodType + ', ' + methodType + 'OData, Base.IBaseExecution & ' + methodType + 'CollectionMethods> & Base.IBaseExecution & ' + methodType + 'CollectionMethods';
} else {
// Generate the interface
return 'Base.IBaseCollection<' + methodType + ', ' + methodType + 'OData>';
}
} else {
// See if methods exist
if (hasCollectionMethods[methodType]) {
// Generate the interface
return 'Base.IBaseCollection<' + methodType + '> & ' + methodType + 'CollectionMethods';
} else {
// Generate the interface
return 'Base.IBaseCollection<' + methodType + '>';
}
}
}
// Generates the base query interface
function generateBaseQuery(methodType, hasCollections, hasMethods) {
let baseQuery = null;
// See if this is a collection
if (/^Array\<.*\>$/.test(methodType)) {
// Update the method type
methodType = methodType.replace(/^Array\</, '').replace(/\>/, '');
}
// See if a collection exists
if (hasCollections[methodType]) {
// Set the base query
baseQuery = 'Base.IBaseQuery<' + methodType + ', ' + methodType + 'OData>';
} else {
// Set the base query
baseQuery = 'Base.IBaseQuery<' + methodType + '>';
}
// Append the collection
baseQuery += ' & ' + methodType + 'Collections';
// See if methods exist
if (hasMethods[methodType]) {
// Append the methods
baseQuery += ' & ' + methodType + 'Methods';
}
// Return the query
return baseQuery;
}
// Recursively read and generate index files
function generateIndexFiles(path) {
let content = [];
let filePath = path + "/index.d.ts";
// See if an index file exists
if (fs.existsSync(filePath)) { return; }
// Read the directory
fs.readdirSync(path).forEach(obj => {
// Skip files
if (/\.d\.ts$/.test(obj)) { return; }
// Append the content
content.push('import * as ' + obj + ' from "./' + obj + '";');
content.push('export { ' + obj + ' }');
// Generate the index files for this directory
generateIndexFiles(path + "/" + obj);
});
// See if content exist
if (content) {
// Create the file
fs.writeFileSync(filePath, content.join('\n'));
}
};
// Method to convert the type
function getType(type = "any", enumInterfaces) {
if (type.indexOf("microsoft.graph") >= 0) {
let typeInfo = type.split('.');
let typeName = typeInfo[typeInfo.length - 1];
if (enumInterfaces[typeName]) {
return "microsoft.graph." + typeName;
}
} else if (type.indexOf("graph.") >= 0) {
let typeInfo = type.split('.');
let typeName = typeInfo[typeInfo.length - 1];
if (enumInterfaces[typeName]) {
return typeName;
}
}
// Return the type
return type
.replace(/Edm\.Boolean/, 'boolean')
.replace(/Edm\.Binary/, 'any')
.replace(/Edm\.Byte/, 'any')
.replace(/Edm\.DateTimeOffset/, 'any')
.replace(/Edm\.DateTime/, 'any')
.replace(/Edm\.Date/, 'any')
.replace(/Edm\.Decimal/, 'number')
.replace(/Edm\.Double/, 'number')
.replace(/Edm\.Duration/, 'number')
.replace(/Edm\.Guid/, 'any')
.replace(/Edm\.Int16/, 'number')
.replace(/Edm\.Int32/, 'number')
.replace(/Edm\.Int64/, 'number')
.replace(/Edm\.Single/, 'any')
.replace(/Edm\.Stream/, 'any')
.replace(/Edm\.String/, 'string')
.replace(/Edm\.TimeOfDay/, 'any')
.replace(/Edm\.Time/, 'any')
.replace(/^Collection\(/, 'Array<')
.replace(/\)$/, '>');
}
// Method to update the references
function updateReferences(fileImports, dirName, type) {
// See if the type requires an import
if (!type.startsWith('{') && type && type.indexOf('.') > 0) {
// Get the last index of it
let refType = type.replace(/^Array\<|\>$/g, '');
refType = refType.substring(0, refType.lastIndexOf('.'));
// Set the root namespace
let root = refType.split('.')[0];
// See if this is the graph
if (root == "graph") {
// Update the name
dirName = dirName.replace("microsoft.graph", "microsoftgraph");
}
// Build the reference to the lib folder
let refPath = "";
for (let j = 0; j < dirName.split('.').length; j++) { refPath += "../"; }
// Add the import
fileImports.push('import { ' + root + ' } from "' + refPath + '";');
}
}
// Process the Graph metadata
function processGraph(schemas) {
let actions = {};
let functions = {};
let collections = {};
let complexTypes = {};
let endPoints = {};
let enums = {};
let enumInterfaces = {};
let entities = {};
let graphEndPoints = {};
// Returns the return type
let convertReturnType = (returnType) => {
// See if this is an edm value, and do nothing
if (returnType.indexOf("Edm") >= 0) { return returnType; }
// Remove microsoft.graph
if (returnType.indexOf("microsoft.graph") >= 0) {
let isCollection = returnType.indexOf("Collection(") == 0;
let info = returnType.split(".")
//return returnType.replace("microsoft.graph.", "").replace(".", "_");
return (isCollection ? "Collection(" : "") + info[info.length - 1];
}
// Remove graph.
if (returnType.indexOf("graph.") == 0) {
return returnType.replace("graph.", "");
}
return returnType;
}
// Parse the schemas
for (let i = 0; i < schemas.length; i++) {
let schema = schemas[i];
// Parse the schema
for (let key in schema) {
let value = schema[key];
// Skip the attributes
if (key == "$") { continue; }
// See if it's an
if (key == "Annotations") {
// Parse the annotations
for (let annotation of value) {
}
// Continue
continue;
}
// See if it's an enumerator
if (key == "EnumType") {
// Parse the enums
for (let enumInfo of value) {
let name = enumInfo.$.Name;
// Add the interface and enum
let enumInterface = "/** " + name + " types */\nexport type " + name + " = {\n";
let enumDef = "/** " + name + " types */\nexport const " + name + ": GraphTypes." + name + " = {\n";
// Parse the members
for (let member of enumInfo.Member) {
// Add the member
enumInterface += "\t" + member.$.Name + ": " + member.$.Value + ";\n"
enumDef += "\t" + member.$.Name + ": " + member.$.Value + ",\n"
}
// Close the interface and enum
enumInterface += "}\n";
enumDef += "}\n"
// Add the enum
enums[name] = enumDef;
enumInterfaces[name] = enumInterface;
}
// Continue
continue;
}
// See if this is a complex type
if (key == "ComplexType") {
// Parse the values
for (let complexType of value) {
let name = complexType.$.Name;
let returnType = complexType.BaseType;
// Parse the properties
let props = [];
let complexTypesProps = complexType.Property || [];
for (let prop of complexTypesProps) {
// Add the property
props.push({ name: prop.$.Name, returnType: prop.$.Type, nullable: prop.$.Nullable });
}
// Add the entity type
complexTypes[name] = { name, returnType, props };
}
}
// See if this is an entity type
if (key == "EntityType") {
// Parse the values
for (let entityType of value) {
let name = entityType.$.Name;
let returnType = entityType.$.BaseType;
// See if the return type exists
if (returnType) {
// Update the return type
//let values = returnType.split(".");
//returnType = values[1] || values[0];
returnType = convertReturnType(returnType);
}
// Parse the properties
let props = [];
let entityProps = entityType.Property || [];
for (let prop of entityProps) {
// Add the property
props.push({ name: prop.$.Name, returnType: convertReturnType(prop.$.Type), nullable: prop.$.Nullable });
}
// Parse the methods
let methods = [];
let subMethods = entityType.NavigationProperty || [];
for (let subMethod of subMethods) {
// Set the method name
let name = subMethod.$.Name;
let nameInfo = name.split('/');
if (nameInfo.length > 1) {
name = nameInfo[nameInfo.length - 1];
}
// Set the return type
//let returnType = convertReturnType(subMethod.$.Type);
let returnType = subMethod.$.Type;
let returnTypeInfo = returnType.split('/');
if (returnTypeInfo.length > 1) {
returnType = returnTypeInfo[returnTypeInfo.length - 1];
}
// Add the method
methods.push({ name, returnType });
// See if this is a collection
if (returnType.startsWith("Collection(")) {
let collectionType = returnType.replace("Collection(", "").replace(")", "");
//collectionType = collectionType.split('.')[1];
collections[collectionType] = returnType;
// Add a method to get an object from the collection by its id
methods.push({
name,
returnType: collectionType,
//returnType2: collectionType + "Methods",
argNames: [{
name: "id",
type: "string | number",
returnType: collectionType
}]
});
}
}
// Add the entity type
entities[name] = { name, returnType, props, methods };
}
// Continue
continue;
}
// See if this is an entity container
if (key == "EntityContainer") {
// Parse the values
for (let val of value) {
// Parse the entity types
let entitySets = val.EntitySet || [];
for (let i = 0; i < entitySets.length; i++) {
let entitySet = entitySets[i];
let name = entitySet.$.Name;
let returnType = entitySet.$.EntityType.replace("microsoft.graph", "").replace(".", "_");
// Parse the methods
let methods = [];
let subMethods = entitySet.NavigationPropertyBinding || [];
for (let subMethod of subMethods) {
// Set the method name
let name = subMethod.$.Path;
let nameInfo = name.split('/');
if (nameInfo.length > 1) {
name = nameInfo[nameInfo.length - 1];
}
// Set the return type
let returnType = subMethod.$.Target;
let returnTypeInfo = returnType.split('/');
if (returnTypeInfo.length > 1) {
returnType = returnTypeInfo[returnTypeInfo.length - 1];
}
// Add the method
methods.push({ name, returnType });
}
// Add the endpoint
endPoints[name] = { name: name, returnType, methods };
}
// Parse the singletons
let singletons = val.Singleton || [];
for (let singleton of singletons) {
let name = singleton.$.Name;
let returnType = singleton.$.Type;
// Parse the methods
let methods = [];
let subMethods = singleton.NavigationPropertyBinding || [];
for (let subMethod of subMethods) {
// Set the method name
let name = subMethod.$.Path;
let nameInfo = name.split('/');
if (nameInfo.length > 1) {
name = nameInfo[nameInfo.length - 1];
}
// Set the return type
let returnType = subMethod.$.Target;
let returnTypeInfo = returnType.split('/');
if (returnTypeInfo.length > 1) {
returnType = returnTypeInfo[returnTypeInfo.length - 1];
}
// Add the method
methods.push({ name, returnType });
}
// Add the endpoint
graphEndPoints[name] = { name, returnType, methods };
// Continue
continue;
}
}
// Continue
continue;
}
// See if this is an action/function
if (key == "Action" || key == "Function") {
// Parse the values
for (let methodInfo of value) {
let bindingParam = null;
let methodName = methodInfo.$.Name;
let methodParams = [];
let methodReturnType = null;
// Get the parameters
let params = methodInfo.Parameter || [];
for (let i = 0; i < params.length; i++) {
let param = params[i];
if (param.$.Name == "bindingParameter") {
bindingParam = param.$.Type;
} else {
methodParams.push(param);
}
}
// Get the return type
if (methodInfo.ReturnType && methodInfo.ReturnType[0]) {
methodReturnType = methodInfo.ReturnType[0].$.Type;
}
// Ensure a binding parameter exists
if (bindingParam) {
// See if this is an action
if (key == "Action") {
actions[bindingParam] = actions[bindingParam] || [];
actions[bindingParam].push({
name: methodName,
params: methodParams,
returnType: methodReturnType
});
} else {
functions[bindingParam] = functions[bindingParam] || [];
functions[bindingParam].push({
name: methodName,
params: methodParams,
returnType: methodReturnType
});
}
}
}
}
}
}
// Method to get the type
let getGraphType = (returnType = "") => {
// See if the return type is in not an Edm
if (returnType.indexOf("graph." == 0)) {
// Set the return type information
let isCollection = returnType.indexOf("Collection(") == 0;
let info = (isCollection ? returnType.replace("Collection(", "").replace(")", "") : returnType);
info = info.indexOf(".") >= 0 ? info.split('.') : info.split('_');
// See if this is a collection using "s" at the end
let name = info[info.length - 1];
let name2 = null;
if (!isCollection && name[name.length - 1] == "s") {
// Set the name
name2 = name.substring(0, name.length - 1);
}
// See if it's a complex type
if (complexTypes[name]) {
// Set the return type
returnType = "ComplexTypes." + name;
} else if (complexTypes[name2]) {
// Set the return type
returnType = "ComplexTypes." + name2 + "[]";
}
// Else, see if it's an entity
else if (entities[name]) {
// Set the return type
returnType = "EntityTypes." + name;
}
// Else, see if it's an entity
else if (entities[name2]) {
// Set the return type
returnType = "EntityTypes." + name2 + "[]";
}
// Else, see if it's an enum
else if (enums[name]) {
// Set the return type
returnType = "EnumTypes." + name;
}
// Else, see if it's an enum
else if (enums[name2]) {
// Set the return type
returnType = "EnumTypes." + name2 + "[]";
}
// Else, see if it's a basic type
else if (info[0] == "Edm" || returnType.indexOf("Edm.") == 0) {
returnType = getType(returnType);
}
// Else, see if this is a column
else if (name == "column") {
returnType = "ComplexTypes.columnDefinition";
}
// Else, see if this is a column array
else if (name == "columns") {
returnType = "ComplexTypes.columnDefinition[]";
} else {
return null;
}
// See if this is a collection, and ensure it's an array
if (isCollection && returnType.indexOf("Array") < 0) {
returnType += "[]";
}
}
// Return the type
return returnType;
}
// Make the graph directory
if (fs.existsSync("lib/microsoft/Graph") == false) { fs.mkdirSync("lib/microsoft/Graph"); }
// Create the endpoints
let content = [
"import { IBaseExecution } from \"../../base\";",
"import * as EntityTypes from \"./entityTypes.d\";\n"
];
for (let name in endPoints) {
let endPoint = endPoints[name];
// Parse the methods
let methods = [];
for (let method of endPoint.methods) {
// Add the method
methods.push("\t" + method.name + ": () => IBaseExecution<" + getGraphType(method.returnType) + ">;");
}
// Add the endpoint
content.push(`/*********************************************
* ${name}
**********************************************/
export interface ${name} extends IBaseExecution<${getGraphType(endPoint.returnType.replace("microsoft.graph.", "graph."))}> {
${methods.join('\n')}
}`);
}
fs.writeFileSync("lib/microsoft/graph/api.d.ts", content.join('\n'));
// Create the enum definitions
content = [];
for (let key in enumInterfaces) { content.push(enumInterfaces[key]); }
fs.writeFileSync("lib/microsoft/graph/enumTypes.d.ts", content.join('\n'));
// Append the export of the enums
fs.appendFileSync("lib/microsoft/graph/index.d.ts", [
'import * as API from "./api";',
'import * as ComplexTypes from "./complexTypes";',
'import * as Enums from "./enumTypes";',
'export { API, ComplexTypes, Enums }',
'export * from "./entityTypes";',
].join('\n'));
// Append the graph endpoint
fs.appendFileSync("lib/Microsoft/index.d.ts", [
'\nimport * as Graph from "./Graph";',
'export { Graph }'
].join('\n'));
// Create the graph enum types
content = ["import * as GraphTypes from \"./microsoft/graph/enumTypes\";\n"];
for (let key in enums) { content.push(enums[key]); }
fs.writeFileSync("lib/enumTypes.ts", content.join('\n'));
// Create the complex types
content = [
"import * as EnumTypes from \"./enumTypes.d\";",
];
for (let name in complexTypes) {
let complexType = complexTypes[name];
// Parse the properties
let props = [];
for (let prop of complexType.props) {
// Add the property
props.push("\t" + prop.name + ": " + getGraphType(prop.returnType) + ";");
}
// Add the endpoint
content.push(`/*********************************************
* ${name}
**********************************************/
export interface ${name} ${complexType.returnType ? "extends " + complexType.returnType : ""} {
${props.join('\n')}
}`);
}
fs.writeFileSync("lib/microsoft/graph/complexTypes.d.ts", content.join('\n').replace(/ComplexTypes./g, ""));
// Create the entities
content = [
"import { IBaseCollection, IBaseExecution, IBaseQuery, IBaseResults } from \"../../base\";",
"import * as ComplexTypes from \"./complexTypes.d\";",
"import * as EnumTypes from \"./enumTypes.d\";"
];
// Create the mapper content
let contentMapper = [];
let contentMapperDef = [];
// Parse the custom methods
for (let name in customV2) {
// Skip collections
if (name.indexOf("Collection") > 0) { continue; }
// See if it exists
if (entities[name]) {
// Append the methods
entities[name].methods = entities[name].methods.concat(customV2[name]);
}
else {
// Add the entry
entities[name] = { props: [], methods: customV2[name] }
}
}
// Get the entity names
let entityNames = [];
for (let name in entities) { entityNames.push(name); }
entityNames = entityNames.sort();
// Parse the entity names
for (let i = 0; i < entityNames.length; i++) {
let name = entityNames[i];
let entity = entities[name];
let baseType = entity.returnType && getGraphType(entity.returnType) ? getGraphType(entity.returnType) + " & " : "";
// See if a collection exists
let collectionInterface = null;
let collectionMapper = "";
let collectionMapperDef = "";
if (collections["graph." + name] && baseType) {
let containsAdd = false;
collectionInterface = `
export interface ${name}Collection extends IBaseCollection<${name}, ${name}OData & ${name}Props> {`
// Parse the methods
let collectionMethods = customV2[name + "Collection"] || [];
for (let j = 0; j < collectionMethods.length; j++) {
// Get the method
let collectionMethod = collectionMethods[j];
containsAdd |= collectionMethod.name == "add";
// Parse the args
let collectionMethodArgs = [];
let collectionMethodArgNames = [];
for (let k = 0; k < collectionMethod.argNames.length; k++) {
let argName = collectionMethod.argNames[k];
collectionMethodArgs.push(argName.name + ": " + argName.type);
collectionMethodArgNames.push(argName.name);
}
// Add the method
collectionInterface += `\n\t${collectionMethod.name}(${collectionMethodArgs.join(', ')}):IBaseExecution<${collectionMethod.returnType || "void"}>`;
// Add the mapper information
collectionMapper += `${collectionMapper ? "\n" : ""}\t\t${collectionMethod.name}: { argNames: ["${collectionMethodArgNames.join('", "')}"], requestType: RequestType.PostBodyNoArgs ${collectionMethod.returnType ? ", returnType: " + collectionMethod.returnType : ""} },`;
collectionMapperDef += `${collectionMapperDef ? "\n" : ""}\t\t${collectionMethod.name}: IMapperMethod${collectionMethodArgs ? " & { argNames: [\"" + collectionMethodArgNames.join('", "') + "\"]" : ""} }`;
}
// Ensure the add method is there by default
if (!containsAdd) {
collectionInterface += `\nadd(values?: any): IBaseExecution<${name}>;`
}
// Add a new line char
collectionInterface += `\n}`;
}
// Get the base return type
let baseReturnType = null;
if (entity.returnType) { baseReturnType = getGraphType(entity.returnType); }
// Parse the properties
let props = [];
let parentReference = null;
for (let prop of entity.props) {
/* Comment out for now. Not sure if we need to set this as a reference or not
if (prop.name == "parentReference") {
// Set the parent reference
parentReference = getGraphType(prop.returnType);
} else {
// Add the property
props.push("\t" + prop.name + ": " + getGraphType(prop.returnType) + ";");
}*/
// Add the property
props.push("\t" + prop.name + ": " + getGraphType(prop.returnType) + ";");
}
// Parse the methods
let contentProperties = [];
let mapper = [];
let mapperDef = [];
let methods = [];
let odataResults = [];
let prevName = null;
for (let method of entity.methods) {
// Add the method
let argNames = method.argNames || [];
let argStrings = [];
let argsMapper = [];
for (let i = 0; i < argNames.length; i++) {
let argName = argNames[i];
argsMapper.push(argName.name);
argStrings.push(argName.name + ": " + argName.type);
}
let returnType = getGraphType(method.returnType, true) || "void";
let isCollection = returnType.indexOf("[]") > 0;
let isComplexType = returnType.indexOf("ComplexTypes.") == 0;
let returnTypeName = returnType.replace(/\[\]$/, '');
let methodsType = returnType == "void" || isComplexType || isCollection ? "" : " & " + returnTypeName + "Methods";
let methodString = `\t${method.name}(${argStrings.join(", ")}): `
if (isCollection) {
let collectionType = returnTypeName.split('.');
collectionType = collectionType[collectionType.length - 1];
if (collections["graph." + collectionType] && getGraphType(entities[collectionType].returnType)) {
methodString += `${collectionType}Collection;`;
} else {
methodString += `IBaseCollection<${returnTypeName}${!isComplexType ? ", " + baseType + returnTypeName + "OData & " + returnTypeName + "Props" : ""}>${methodsType}${method.returnType2 && getGraphType(method.returnType2, true) ? " & " + getGraphType(method.returnType2, true) : ""};`
}
} else {
methodString += `${returnTypeName == "void" ? "IBaseExecution" : "IBaseQuery"}<${returnTypeName}>${methodsType}${method.returnType2 && getGraphType(method.returnType2, true) ? " & " + getGraphType(method.returnType2, true) : ""};`
};
methods.push(methodString);
// Ensure we haven't already added it
if (method.name != prevName) {
// Add the mapper
let mapperReturnType = returnTypeName.split('.');
mapperReturnType = mapperReturnType[mapperReturnType.length - 1];
argsMapperStr = argsMapper.length > 0 ? `"${argsMapper.join('", "')}"` : "";
mapper.push(`\t\t${method.name}: {\n${argsMapperStr ? "\t\t\targNames: [" + argsMapperStr + "],\n" : ""}${isCollection ? "\t\t\trequestType: RequestType.Get,\n" : ""}${mapperReturnType != "void" ? "\t\t\treturnType: \"" + mapperReturnType + (isCollection ? "s" : "") + "\"\n" : ""}\t\t},`)
mapperDef.push(`\t\t${method.name}: IMapperMethod${argsMapperStr ? " & {\n\t\t\targNames: [" + argsMapperStr + "]\n\t\t}" : ""};`);
// See if it has args
if (argStrings.length > 0) {
// Add the odata result
odataResults.push(methodString);
} else {
// Add the odata result
odataResults.push(`\t${method.name}: ${isCollection ? "IBaseResults<" : ""}${returnTypeName}${isCollection ? ">" : ""};`);
}
}
// Else, see if this is a collection property
else if (argNames.length == 1) {
let mapperReturnType = returnTypeName.split('.');
mapperReturnType = mapperReturnType[mapperReturnType.length - 1];
// Add the collection property
contentProperties.push(`${method.name}|${mapperReturnType}s|/[Name]|${mapperReturnType}`);
}
// Set the previous name
prevName = method.name;
}
// Parse the actions
let entityActions = actions["graph." + name];
if (entityActions) {
for (let i = 0; i < entityActions.length; i++) {
let entityAction = entityActions[i];
let returnType = entityAction.returnType ? getGraphType(entityAction.returnType) : null;
let prevMatch = i > 0 && entityActions[i - 1].name == entityAction.name ? true : false;
// Parse the arguments
let params = [];
let paramDefs = [];
for (let j = 0; j < entityAction.params.length; j++) {
let param = entityAction.params[j];
// Add the parameter
params.push(param.$.Name);
paramDefs.push(`${param.$.Name}: ${getGraphType(param.$.Type)}`);
}
// Add the method to the mapper and definition
let strMapper = `\t\t${entityAction.name}: {\n${params.length > 0 ? "\t\t\targNames: [\"" + params.join('", "') + "\"],\n" : ""}\t\t\trequestType: RequestType.PostWithArgsInBody,\n${returnType ? "\t\t\treturnType: \"" + returnType + "\"\n" : ""}\t\t},`;
let strMapperDef = `\t\t${entityAction.name}: IMapperMethod${params.length > 0 ? " & {\n\t\t\targNames: [\"" + params.join('", "') + "\"]\n\t\t}" : ""};`;
if (prevMatch) {
mapper[mapper.length - 1] = strMapper;
mapperDef[mapperDef.length - 1] = strMapperDef;
} else {
mapper.push(strMapper);
mapperDef.push(strMapperDef);
}
// Add the method
let strMethod = `\t${entityAction.name}(${params.join(", ")}): IBaseExecution<${returnType || "void"}>;`;
methods.push(strMethod);
odataResults.push(strMethod);
}
}
// Parse the functions
let entityFunctions = functions["graph." + name];
if (entityFunctions) {
for (let i = 0; i < entityFunctions.length; i++) {
let entityFunction = entityFunctions[i];
let returnType = entityFunction.returnType ? getGraphType(entityFunction.returnType) : null;
let prevMatch = i > 0 && entityFunctions[i - 1].name == entityFunction.name ? true : false;
// Parse the arguments
let params = [];
let paramDefs = [];
for (let j = 0; j < entityFunction.params.length; j++) {
let param = entityFunction.params[j];
// Add the parameter
params.push(param.$.Name);
paramDefs.push(`${param.$.Name}: ${getGraphType(param.$.Type)}`);
}
// Add the method to the mapper and definition
let strMapper = `\t\t${entityFunction.name}: {\n${params.length > 0 ? "\t\t\targNames: [\"" + params.join('", "') + "\"],\n" : ""}\t\t\trequestType: RequestType.Get,\n${returnType ? "\t\t\treturnType: \"" + returnType + "\"\n" : ""}\t\t},`;
let strMapperDef = `\t\t${entityFunction.name}: IMapperMethod${params.length > 0 ? " & {\n\t\t\targNames: [\"" + params.join('", "') + "\"]\n\t\t}" : ""};`;
if (prevMatch) {
mapper[mapper.length - 1] = strMapper;
mapperDef[mapperDef.length - 1] = strMapperDef;
} else {
mapper.push(strMapper);
mapperDef.push(strMapperDef);
}
// Add the method
let strMethod = `\t${entityFunction.name}(${params.join(", ")}): ${returnType || "void"};`;
methods.push(strMethod);
odataResults.push(strMethod);
}
}
// Add the endpoint
/**
* Commented out for now. Not sure if we need to use the parent reference or not
* export interface ${name}Props${parentReference || baseReturnType ? " extends" : ""} ${parentReference ? parentReference : ""} ${parentReference && baseReturnType ? ", " : ""}${baseReturnType ? baseReturnType + "Props" : ""} {
*/
content.push(`/*********************************************
* ${name}
**********************************************/
export interface ${name} extends ${name}Props, ${name}Methods { }
export interface ${name}Props${baseReturnType ? " extends " + baseReturnType + "Props" : ""} {
${props.join('\n')}
}
export interface ${name}Methods${baseReturnType ? " extends " + baseReturnType + "Methods" : ""} {
${methods.join('\n')}
}
export interface ${name}OData${baseReturnType ? " extends " + baseReturnType + "OData" : ""} {
${odataResults.join('\n')}
}${collectionInterface || ""}`);
// Add the mapper
contentMapper.push(`\t${name}: {
\t\tproperties: [
${contentProperties.length > 0 ? '\t\t\t"' + contentProperties.join('", "') + '"' : ""}
\t\t],
${name != "searchEntity" ? '\t\tquery: { argNames: ["oData"], requestType: RequestType.OData },' : ""}
${mapper.join('\n')}
\t},`);
// Add the mapper
contentMapperDef.push(`\t${name}: {
\t\tproperties?: Array<string>;
${name != "searchEntity" ? '\t\tquery: IMapperMethod & { argNames: ["oData"] };' : ""}
${mapperDef.join('\n')}
\t},`);
// See if this is a collection
if (collectionInterface && name != "site") {
// Add the collection to the mapper
contentMapper.push(`\t${name}s: {
\t\tquery: { argNames: ["oData"], requestType: RequestType.OData },${collectionMapper ? "\n" + collectionMapper : ""}
\t},`)
contentMapperDef.push(`\t${name}s: {
\t\tproperties?: Array<string>;
\t\tquery: IMapperMethod & { argNames: ["oData"] }${collectionMapperDef ? "\n" + collectionMapperDef : ""}
\t},`);
}
}
fs.writeFileSync("lib/microsoft/graph/entityTypes.d.ts", content.join('\n').replace(/EntityTypes./g, ""));
// Create the mapper files
fs.appendFileSync('lib/mapperv2.d.ts', [
'import { IMapperMethod } from "./base";\n\n',
'/** Mapper */',
'export interface IMapper {',
contentMapperDef.join('\n'),
'}'
].join('\n'));
fs.appendFileSync('lib/mapperv2.ts', [
'import { IMapper } from "./mapperv2.d";',
'import { RequestType } from "../utils";',
'export const Mapper: IMapper = {',
contentMapper.join('\n'),
'}'
].join('\n'));
}
// Process the REST metadata
function processREST(schemas) {
let directories = {};
let enumInterfaces = {};
let methods = {};
let methodTypes = {};
// Parse the schemas
for (let i = 0; i < schemas.length; i++) {
let schema = schemas[i];
// Ensure a namespace name exists
let ns = schema.$ ? schema.$.Namespace : null;
if (ns) {
// Set the directory name
directories[ns] = directories[ns] || {};
directories[ns]._api = [];
} else { continue; }
// Parse the schema
for (let key in schema) {
let value = schema[key];
// Skip the attributes
if (key == "$") { continue; }
// See if this is a collection
if (value.length > 0) {
let collection = key + "s";
let isAssociation = key == "Association";
let isEntityContainer = key == "EntityContainer";
// Parse the collection
for (let j = 0; j < value.length; j++) {
let interface = value[j];
// See if this is an association
if (isAssociation) {
// Parse the end points
let endPoints = interface.End || [];
for (let k = 0; k < endPoints.length; k++) {
let endPoint = endPoints[k];
// Get the method information
let isCollection = endPoint.$ ? endPoint.$.Multiplicity == '*' : false;
let name = interface.$ ? interface.$.Name : null;
let role = endPoint.$ ? endPoint.$.Role : null;
let type = endPoint.$ ? endPoint.$.Type : null;
// Ensure the name, role and type exist
if (name && role && type) {
// Ensure method and name types exist
methodTypes[name] = methodTypes[name] || {};
// Add the method
methodTypes[name][role] = { isCollection, type };
}
}
}
// Else, see if this is an entity container
else if (isEntityContainer) {
// Parse the functions
let functions = interface.FunctionImport || [];
for (let k = 0; k < functions.length; k++) {
let functionInfo = functions[k];
// See if this is an endpoint
if (functionInfo.$.IsComposable && functionInfo.$.IsBindable != "true") {
// Add the endpoint
directories[ns]._api.push(functionInfo.$);
continue;
}
// See if this function has the valid information
let isValid = functionInfo.Parameter ? functionInfo.Parameter[0] : null;
isValid = isValid ? isValid.$ : null;
isValid = functionInfo.$ && isValid && isValid.Name == "this" && isValid.Type ? true : false;
if (!isValid) { continue; }
// Get the function information
let name = functionInfo.$.Name[0].toLowerCase() + functionInfo.$.Name.slice(1);
let returnType = getType(functionInfo.$.ReturnType, enumInterfaces);
let parentName = functionInfo.Parameter[0].$.Type;
// Create an array for the methods
methods[parentName] = methods[parentName] || [];
methods[parentName].push({ name, returnType, params: functionInfo.Parameter.slice(1) });
}
} else {
// Add the collection
directories[ns][collection] = directories[ns][collection] || {};
// Validate the collection
let name = interface.$ ? interface.$.Name : null;
if (name) {
// Add the interface
directories[ns][collection][name] = {};
// See if the base type exists and doesn't reference itself
if (interface.$.BaseType && !interface.$.BaseType.endsWith(name)) {
// Set the base type
directories[ns][collection][name]._BaseType = interface.$.BaseType;
}
// Parse the methods
let methods = interface.NavigationProperty || [];
for (let k = 0; k < methods.length; k++) {
let method = methods[k];
// Get the method information
let methodName = method.$ ? method.$.Name : null;
let methodRole = method.$ ? method.$.ToRole : null;
let methodType = method.$ ? method.$.Relationship : null;
// Validate the method information
if (methodName && methodRole && methodType) {
// Ensure collection name exist
directories[ns][collection][name]._Collections = directories[ns][collection][name]._Collections || {};
directories[ns][collection][name]._Collections[methodName] = directories[ns][collection][name]._Collections[methodName] || {};
// Add the method
directories[ns][collection][name]._Collections[methodName][methodRole] = methodType;
} else { continue; }
}
// Parse the properties
let properties = interface.Property || [];
for (let k = 0; k < properties.length; k++) {
let property = properties[k];
// Ensure a name exists
let propName = property.$ ? property.$.Name : null;
if (propName) {
// Create the property