npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

pir-model-preparer

v2.2.3

Published

Model Preparer (converter from PIR-XSD to Sencha ExtJS Model Classes)

Downloads

5

Readme

Конвертация моделей данных из формата XSD ПИР в формат Sencha Ext JS Model

Инсталяция для пользователя

Установить SWI-Prolog 7.4.2 for Microsoft Windows (64 bit)
http://www.swi-prolog.org/download/stable
Добавить в PATH строку C:\Program Files\swipl\bin.
После этого в командной строке должна быть доступна команда swipl.

Установить зависимости
npm install

Создать пустой пакет (здесь он называется pir-model), где будут размещаться сгененированные модели.

В файле package.json вашего проекта прописать скрипт (здесь он называется pir-model):

"scripts": {
   "pir-model": "node_modules/.bin/pir-model-preparer -c packages/local/pir-model/preparer.config.js"
}

В корне пакета pir-model создать файл preparer.config.js со следующим содержимым:

const extend = require('./preparer/extend');
const prepareClasses = require('./preparer/prepareClasses');

module.exports = {

	dirs: {
		input: '<путь до WSDL-файлов>',
		output: {
			result: 'src'
		}
	},

	namespace: 'Pir.model',
	extend: extend,
	prepareClasses: prepareClasses

};

Здесь namespace укажите для своего проекта.

Создайте файл \packages\local\pir-model\preparer\extend.js со следующим содержимым:

/**
 * Вычисление опции extend для каждой модели.
 */
module.exports = function(model) {
	const m = model, bt = model.baseType;
	return model.baseType ? `${m.namespace}.${bt.path.join('.')}.${bt.className}` : `${m.namespace}.Base`;
};

Создайте файл \packages\local\pir-model\preparer\prepareClasses.js со следующим содержимым:

const fs = require('fs');
const path = require('path');
const _ = require('lodash');
const senchaExtjsGenerator = require('sencha-extjs-generator');

const ModelClass = senchaExtjsGenerator.ModelClass;
const Config = senchaExtjsGenerator.Config;

/**
 * Изменение классов перед их сохранением в пакете pir-model.
 */
module.exports = function(modelClassManager) {
	const common = require('./common')(modelClassManager);

	/**
	 * Создание дополнительных классов.
	 */
	const creators = fs.readdirSync(path.join(__dirname, 'create'));
	creators.forEach(creatorFilename => {
		const creatorFilepath = path.join(__dirname, 'create', creatorFilename);
		// Криэтор класса может создавать как один класс, так и массив классов.
		const cls = require(creatorFilepath)(common);
		modelClassManager.push(cls);
		// Любой класс может содержать функцию _onAfterModelClassManagerPush.
		// Если он есть, то считаем, что класс предполагает обработку после его добавления в менеджер классов.
		(_.isArray(cls) ? cls : [cls]).forEach(cls => {
			if (cls._onAfterModelClassManagerPush) cls._onAfterModelClassManagerPush();
		});
	});

	/**
	 * Переопределение имеющихся классов Override.
	 * Этим добавляется дополнительный функционал в имеющиеся модели данных.
	 */
	const overrides = fs.readdirSync(path.join(__dirname, 'override'));
	overrides.forEach(overrideFilename => {
		const overrideFilepath = path.join(__dirname, 'override', overrideFilename);
		require(overrideFilepath)(common);
	});

};

Создайте файл \packages\local\pir-model\preparer\common.js со следующим содержимым:


const camelcase = require('camelcase');
const pascalcase = require('pascalcase');
const { Config: ExtjsConfig, ModelClass: ExtjsModelClass } = require('sencha-extjs-generator');

/**
 * Общие функции.
 */
module.exports = function(modelClassManager) {

	function getModelClassManager() {
		return modelClassManager;
	}

	/**
	 * Функция для удобной обработки одного класса.
	 * В качестве контекста this для функции prepareFn выступает сам класс.
	 */
	function prepareClass(className, prepareFn) {
		const cls = modelClassManager.find(className);
		if (!cls) console.error('[ERR] prepareClass: Не найден класс "' + className + '".');
		if (cls) prepareFn.bind(cls)(cls);
	}
	
	/**
	 * Функция для создания классов, предназначенных для создания хранилищ 
	 * под результаты разных методов АПИ сервера.
	 * Добавляет название метода сервиса и имя поля, где хранятся массив записей.
	 * @return {ExtjsModelClass}
	 */
	function createInfoClass({ baseClassName, name, serviceMethod, rootProperty }) {
		const baseClass = modelClassManager.find(baseClassName);
		const infoClass = baseClass.clone();
		infoClass.name.value = name;
		infoClass.extend.value = baseClass.name.localName;
		infoClass.proxy.value = new ExtjsConfig([{
			name: 'serviceMethod',
			value: serviceMethod
		}, {
			name: 'reader',
			value: new ExtjsConfig([{
				name: 'rootProperty',
				value: `result.${rootProperty}`
			}])
		}]);
		// Внимание, после добавления класса в менеджер классов добавьте очистку полей:
		// infoClass.fields.clear();
		// Очистку полей можно делать ТОЛЬКО после добавления класса в менеджер классов.
		return infoClass;
	}

	/**
	 * Вычисление baseClassName и rootProperty для классов информационных моделей.
	 * На выходе готовый конфиг для метода createInfoClass.
	 * @param {Object} params
	 * @param {String} params.name Имя класса без неймспейса, например info.DebtorInfoSearchTemplate.
	 * @param {String} params.serviceMethod Имя метода в формате camelCase, например, getManagementCompanySearchTemplates.
	 * @param {String} params.serviceName Имя сервиса = administration | operations | information.
	 * @return {Object}
	 */
	function createInfoClassConfig({ name, serviceMethod, serviceName }) {
		let rootProperty, baseClassName;

		// Имя сервиса в WSDL (точнее имена файлов *.wsdl) отличаются от именования сервисов в клиенте.
		const serviceFolderName = {
			information: 'debtorInformation',
			operations: 'debtorOperations',
			administration: 'debtorAdministration'
		}[serviceName];
		if (!serviceFolderName) throw new Error(`Неизвестный сервис '${serviceName}'.`);
		
		// Ищем класс ответа для данного метода сервера.
		const responseClassName = `${serviceFolderName}.${pascalcase(serviceMethod)}Response`;
		const responseClass = modelClassManager.find(responseClassName);
		if (!responseClass) throw new Error(`Не найден класс '${responseClassName}'.`);
		if (!responseClass.has('hasOne')) throw new Error(`В классе ответа сервера '${responseClassName}' нет hasOne с role==result.`);

		// В классе ответа ищем поле result в разделе hasOne.
		const resultAssociation = responseClass.hasOne.value.find(item => item.find('role').value == 'result');
		// В поле result ищем type. Это будет класс результатов в ответе сервера.
		const resultClassName = resultAssociation.find('type').value;
		const resultClass = modelClassManager.find(resultClassName);
		if (!resultClass) throw new Error(`Не найден класс '${resultClassName}'.`);
		// В классе результата ищем hasMany.
		// Для проверки надо убедится что он есть и в нем только одна запись, иначе ошибка.
		if (!resultClass.has('hasMany')) throw new Error(`В классе результата '${resultClassName}' нет hasMany.`);
		if (resultClass.hasMany.value.length > 1) throw new Error(`В hasMany класса результата '${resultClassName}' больше одной связи.`);
		
		// Также можно проверить наличие totalRecordCount в fields, иначе ошибка.
		if (!resultClass.has('fields')) throw new Error(`В классе '${resultClassName}' нет полей модели.`);
		if (!resultClass.fields.value.find(field => field.find('name').value == 'totalRecordCount')) {
			throw new Error(`В классе '${resultClassName}' не найдено поле totalRecordCount.`);
		}

		// Из hasMany следует вытащить role и сделать его PascalCase.
		rootProperty = pascalcase(resultClass.hasMany.value[0].find('role').value);
		baseClassName = resultClass.hasMany.value[0].find('type').value;

		return { baseClassName, name, serviceMethod, rootProperty, serviceName };
	}

	/**
	 * Создание класса модели для словарей.
	 * Все классы наследуются от класса результата ответа сервера на метод getDictionaryDetailsBySearchParams
	 * На данный момент этим классом является Pir.model.baseModel.type.TBaseDictionary.
	 * Также в extraParams.DictionaryCode включен код словаря dictionaryCode.
	 */
	function createDictionaryDetailClass(dictionaryCode) {

		const serviceMethod = 'getDictionaryDetailsBySearchParams';

		const { baseClassName, rootProperty } = createInfoClassConfig({
			serviceName: 'information', serviceMethod
		});

		const dictionaryDetailClass = new ExtjsModelClass(`dictionary.${dictionaryCode}`, [{
			name: 'proxy',
			value: new ExtjsConfig([{
				name: 'serviceMethod',
				value: serviceMethod
			}, {
				name: 'reader',
				value: {
					name: 'rootProperty',
					value: rootProperty
				}
			}, {
				name: 'extraParams',
				value: {
					name: 'DictionaryCode',
					value: dictionaryCode
				}
			}])
		}]);

		dictionaryDetailClass.extend.value = baseClassName;

		return dictionaryDetailClass;
	}

	return {
		prepareClass,
		createInfoClass,
		createInfoClassConfig,
		createDictionaryDetailClass
	};

}

Создайте каталоги:
\packages\local\pir-model\preparer\create
\packages\local\pir-model\preparer\override
с содержимым аналогичным как в проекте w_pir_client_v2.

Собственно кодогенерацию следует настраивать путем создания скриптов в этих двух директориях.
В директории create создаются скрипты для создания новых моделей.
В директории override создаются оверрайды для существующих.

Запуск кодогенерации

npm run pir-model

Если все было сделано верно, сгенерированные модели будут размещены в директории \packages\local\pir-model\src.