UNPKG

angular-swagger-ui

Version:
1,683 lines (1,563 loc) 62.4 kB
/* * Orange angular-swagger-ui - v0.6.5 * * (C) 2015 Orange, all right reserved * MIT Licensed */ 'use strict'; angular .module('swaggerUi', ['ng']) .directive('swaggerUi', ["$injector", function($injector) { return { restrict: 'A', controller: 'swaggerUiController', templateUrl: function(element, attrs) { return attrs.templateUrl || 'templates/swagger-ui.html'; }, scope: { // OpenApi specification URL (string, required) url: '=?', // OpenApi specification parser type (string, optional, default = "auto") // Built-in allowed values: // "auto": (default) parser is based on response Content-Type // "json": force using JSON parser // "yaml": force using YAML parser, requires to use module 'swagger-yaml-parser' // // More types could be defined by external modules parser: '@?', // OpenApi specification loading indicator (variables, optional) loading: '=?', // Use permalinks? (boolean, optional, default = false) permalinks: '=?', // Display link to download swagger file (empty or i18n label, default = null) download: '@?', // Display API explorer (boolean, optional, default = false) apiExplorer: '=?', // Error handler (function, optional) errorHandler: '=?', // Are OpenApi specifications loaded from trusted source only ? (boolean, optional, default = false) // If true, it avoids using ngSanitize but consider HTML as trusted so won't be cleaned trustedSources: '=?', // Allows defining a custom OpenApi validator or disabling OpenApi validation // If false, OpenApi validation will be disabled // If URL, will be used as OpenApi validator // If not defined, validator will be 'http://online.swagger.io/validator' validatorUrl: '@?', // Specifies the type of "input" parameter to allow rendering OpenApi specification from object or string (string, optional) // Allowed values: // "url": (default) "input" parameter is an URL // "json": "input" parameter is a JSON object // "yaml": "input" parameter is a YAML string, requires to use module 'swagger-yaml-parser' // inputType: '@?', // Allows rendering an external OpenApi specification (string or object, optional) input: '=?', // When displaying polymorphic models, show inherited properties ? (boolean, optional, default = false) showInheritedProperties: '=?' }, link: function(scope) { // check parameters if (!scope.trustedSources && !$injector.has('$sanitize')) { console.warn('AngularSwaggerUI: You must use ngSanitize OR set trusted-sources=true as directive param if OpenApi specifications are loaded from trusted sources'); } if (scope.validatorUrl === undefined) { scope.validatorUrl = 'http://online.swagger.io/validator'; } if (typeof scope.download === 'undefined') { scope.download = null; } } }; }]); /* * Orange angular-swagger-ui - v0.6.5 * * (C) 2015 Orange, all right reserved * MIT Licensed */ 'use strict'; angular .module('swaggerUi') .controller('swaggerUiController', ["$scope", "$window", "$location", "$anchorScroll", "$timeout", "$sce", "$q", "swaggerClient", "swaggerModules", "swaggerTranslator", "swaggerLoader", "swaggerModel", function($scope, $window, $location, $anchorScroll, $timeout, $sce, $q, swaggerClient, swaggerModules, swaggerTranslator, swaggerLoader, swaggerModel) { var openApiSpec; /** * OpenApi specification has been loaded, launch parsing */ function swaggerLoaded(url, data) { $scope.loading = false; swaggerModel.clearCache(); // execute modules swaggerModules .execute(swaggerModules.PARSE, data) .then(function(executed) { if (executed) { swaggerParsed(data); } else { onError({ code: 415, message: swaggerTranslator.translate('errorNoParserFound', { type: data.contentType }) }); } }) .catch(onError); } /** * OpenApi specification has been parsed, launch display */ function swaggerParsed(data) { // execute modules swaggerModules .execute(swaggerModules.BEFORE_DISPLAY, data) .then(function() { // display swagger UI openApiSpec = data.openApiSpec; $scope.ui = data.ui; if ($scope.permalinks) { $timeout(function() { $anchorScroll(); }, 200); } }) .catch(onError); } function onError(error) { console.error('AngularSwaggerUI', error); $scope.loading = false; if (typeof $scope.errorHandler === 'function') { $scope.errorHandler(error.message, error.code); } } function watchData() { return $scope.$watch('input', function(spec) { reset(); if (spec) { $scope.loading = true; swaggerLoaded(null, { openApiSpec: angular.copy(spec), parser: $scope.parser || $scope.inputType || 'json', contentType: 'application/' + ($scope.inputType || 'json') }); } }); } function watchUrl(key) { return $scope.$watch(key, function(url) { reset(); if (url && url !== '') { if ($scope.loading) { //TODO cancel current loading spec } if ($scope.validatorUrl && url.indexOf('http') !== 0) { // make URL absolute to make validator working $scope.url = absoluteUrl(url); return; } // load OpenApi specification $scope.loading = true; var data = { url: url, parser: $scope.parser || 'auto', trustedSources: $scope.trustedSources }; swaggerLoader .get(data) .then(function() { return swaggerModules.execute(swaggerModules.BEFORE_PARSE, data); }) .then(function() { swaggerLoaded(url, data); }) .catch(onError); } }); } function reset() { $scope.ui = { form: {}, infos: {}, resources: [] }; openApiSpec = null; } function watchInputType() { var unwatch; $scope.$watch('inputType', function(inputType) { reset(); if (unwatch) { unwatch(); } switch (inputType) { case 'json': case 'yaml': $scope.validatorUrl = false; // disable validator unwatch = watchData(); break; case 'url': unwatch = watchUrl('input'); break; default: unwatch = watchUrl('url'); break; } }); } watchInputType(); /** * transform a relative URL to an absolute URL */ function absoluteUrl(url) { var a = document.createElement('a'); a.href = url; return a.href; } /** * show all resource's operations as list or as expanded list */ $scope.expand = function(resource, expandOperations) { resource.open = true; for (var i = 0, op = resource.operations, l = op.length; i < l; i++) { op[i].open = expandOperations; } }; $window.swaggerlink = $scope.permalink = function(name) { if ($scope.permalinks) { $timeout(function() { $location.hash(name); $anchorScroll(name); }, 50); } }; /** * sends a sample API request */ $scope.submitExplorer = function(operation) { operation.loading = true; swaggerClient .send(openApiSpec, operation, $scope.ui.form[operation.id], getSecurityDefinitions(operation)) .then(function(result) { operation.loading = false; operation.explorerResult = result; }); }; function getSecurityDefinitions(operation) { var i = 0, security, key, operationSecurityDefinitions = {}, securities = operation.security || []; if (openApiSpec) { for (; i < securities.length; i++) { security = securities[i]; for (key in security) { operationSecurityDefinitions[key] = openApiSpec.securityDefinitions[key]; } } } return operationSecurityDefinitions; } /** * handle operation's authentication params */ $scope.auth = function(operation) { swaggerModules .execute(swaggerModules.AUTH, { securityDefinitions: getSecurityDefinitions(operation) }) .catch(onError); }; /** * check if operation's authorization params are set */ $scope.authValid = function(operation) { var key, securityDefinitions = getSecurityDefinitions(operation); for (key in securityDefinitions) { if (!securityDefinitions[key].valid) { return false; } } return true; }; $scope.getModel = function(obj, operationId, section) { var id = operationId + '-' + section; obj.models = obj.models || {}; if (!obj.models[id] && obj.schema) { obj.models[id] = $sce.trustAsHtml(swaggerModel.generateModel(openApiSpec, obj.schema, id, $scope.showInheritedProperties)); } return obj.models[id]; }; $scope.getSample = function(obj, contentType) { obj.samples = obj.samples || {}; if (contentType && !obj.samples[contentType] && obj.schema) { obj.samples[contentType] = swaggerModel[contentType.indexOf('/xml') >= 0 ? 'generateSampleXml' : 'generateSampleJson'](openApiSpec, obj.schema, obj.examples && obj.examples[contentType]); } return obj.samples[contentType]; }; }]); /* * Orange angular-swagger-ui - v0.6.5 * * (C) 2015 Orange, all right reserved * MIT Licensed */ 'use strict'; angular .module('swaggerUi') .directive('fileInput', function() { // helper to be able to retrieve HTML5 File in ngModel from input return { restrict: 'A', require: 'ngModel', link: function(scope, element, attr, ngModel) { element.bind('change', function() { scope.$apply(function() { //TODO manage multiple files ? ngModel.$setViewValue(element[0].files[0]); }); }); } }; }); /* * Orange angular-swagger-ui - v0.6.5 * * (C) 2015 Orange, all right reserved * MIT Licensed */ 'use strict'; angular .module('swaggerUi') .directive('selectOnDbClick', ["$window", function($window) { // helper to be able select all text on double click return { restrict: 'A', link: function(scope, element) { element.bind('dblclick', function() { var selection = $window.getSelection(), range = document.createRange(); range.selectNodeContents(element[0]); selection.removeAllRanges(); selection.addRange(range); }); } }; }]); /* * Orange angular-swagger-ui - v0.6.5 * * (C) 2015 Orange, all right reserved * MIT Licensed */ 'use strict'; angular .module('swaggerUi') .service('swaggerClient', ["$q", "$http", "$httpParamSerializer", "swaggerModules", function($q, $http, $httpParamSerializer, swaggerModules) { /** * format API explorer response before display */ function formatResult(deferred, response) { var query = '', data = response.data, config = response.config; if (config.params) { var parts = []; for (var key in config.params) { parts.push(key + '=' + encodeURIComponent(config.params[key])); } if (parts.length > 0) { query = '?' + parts.join('&'); } } deferred.resolve({ url: config.url + query, response: { body: data ? (angular.isString(data) ? data : angular.toJson(data, true)) : 'no content', status: response.status, headers: angular.toJson(response.headers(), true) } }); } /** * Send API explorer request */ this.send = function(openApiSpec, operation, values, securityDefinitions) { var deferred = $q.defer(), query = {}, headers = {}, path = operation.path, urlEncoded = values.contentType === 'application/x-www-form-urlencoded', body; // in case path contains request URI template (RFC-6570 https://tools.ietf.org/html/rfc6570) if (path.match(/.*\?.*({(\?|&).*})/)) { path = path.split('?')[0]; } else if (path.match(/.*({(\?).*})/)) { path = path.split('{?')[0]; } // build request parameters for (var i = 0, params = operation.parameters || [], l = params.length; i < l; i++) { //TODO manage 'collectionFormat' (csv etc.) !! var param = params[i], value = values[param.name]; switch (param.in) { case 'query': if (!!value) { query[param.name] = value; } break; case 'path': path = path.replace(new RegExp('{' + param.name + '[^}]*}'), encodeURIComponent(value)); break; case 'header': if (!!value) { headers[param.name] = value; } break; case 'formData': body = body || (urlEncoded ? {} : new FormData()); if (!!value) { if (param.type === 'file') { values.contentType = undefined; // make browser defining it by himself } if (urlEncoded) { body[param.name] = value; } else { body.append(param.name, value); } } break; case 'body': body = body || value; break; } } // authorizations angular.forEach(securityDefinitions, function(security) { if (security.valid) { switch (security.type) { case 'apiKey': switch (security.in) { case 'header': headers[security.name] = security.apiKey; break; case 'query': query[security.name] = security.apiKey; break; } break; default: if (security.tokenType && security.accessToken) { headers.Authorization = security.tokenType + ' ' + security.accessToken; } break; } } }); // add headers headers.Accept = values.responseType; headers['Content-Type'] = body ? values.contentType : 'text/plain'; // build request var basePath = openApiSpec.basePath || '', baseUrl = [ openApiSpec.schemes[0], '://', openApiSpec.host, basePath.length > 0 && basePath.substring(basePath.length - 1) === '/' ? basePath.slice(0, -1) : basePath ].join(''), options = { method: operation.httpMethod, url: baseUrl + path, headers: headers, data: urlEncoded ? $httpParamSerializer(body) : body, params: query }, callback = function(result) { // execute modules var response = { data: result.data, status: result.status, headers: result.headers, config: result.config }; swaggerModules .execute(swaggerModules.AFTER_EXPLORER_LOAD, response) .then(function() { formatResult(deferred, response); }); }; // execute modules swaggerModules .execute(swaggerModules.BEFORE_EXPLORER_LOAD, options) .then(function() { // send request $http(options) .then(callback) .catch(callback); }); return deferred.promise; }; }]); /* * Orange angular-swagger-ui - v0.6.5 * * (C) 2015 Orange, all right reserved * MIT Licensed */ 'use strict'; angular .module('swaggerUi') .provider('swaggerTranslator', function() { var currentLang = 'en', allTranslations = {}; /** * set startup language */ this.setLanguage = function(lang) { currentLang = lang; return this; }; /** * add translations for a specific language code */ this.addTranslations = function(lang, translations) { var map = allTranslations[lang] = allTranslations[lang] || {}; angular.merge(map, translations); return this; }; this.$get = ["$rootScope", "$interpolate", function($rootScope, $interpolate) { return { /** * change current used language */ useLanguage: function(lang) { if (typeof allTranslations[lang] === 'undefined') { console.error('AngularSwaggerUI: No translations found for language '+lang); } currentLang = lang; $rootScope.$emit('swaggerTranslateLangChanged'); }, /** * get a localized message */ translate: function(key, values) { if (currentLang && allTranslations && allTranslations[currentLang] && allTranslations[currentLang][key]) { return $interpolate(allTranslations[currentLang][key])(values); } else { return key; } }, /** * get current used language */ language: function() { return currentLang; } }; }]; }) .directive('swaggerTranslate', ["$rootScope", "$parse", "swaggerTranslator", function($rootScope, $parse, swaggerTranslator) { return { restrict: 'A', link: function(scope, element, attributes) { function translate() { var params; if (attributes.swaggerTranslateValue) { params = $parse(attributes.swaggerTranslateValue)(scope.$parent); } element.text(swaggerTranslator.translate(attributes.swaggerTranslate, params)); } var unbind = $rootScope.$on('swaggerTranslateLangChanged', function() { translate(); }); scope.$on('$destroy', unbind); translate(); } }; }]) .filter('swaggerTranslate', ["swaggerTranslator", function(swaggerTranslator) { var filter = function(input, option) { return swaggerTranslator.translate(input, option); }; filter.$stateful = true; return filter; }]); /* * Orange angular-swagger-ui - v0.6.5 * * (C) 2015 Orange, all right reserved * MIT Licensed */ 'use strict'; angular .module('swaggerUi') .service('swaggerLoader', ["$q", "$http", "swaggerModules", function($q, $http, swaggerModules) { this.get = function(data) { var deferred = $q.defer(), options = { method: 'GET', url: data.url }; swaggerModules .execute(swaggerModules.BEFORE_LOAD, options) .then(function() { return $http(options); }) .then(function(response) { data.openApiSpec = response.data; data.contentType = (response.headers()['content-type'] || 'application/json').split(';')[0]; return swaggerModules.execute(swaggerModules.AFTER_LOAD, data); }) .then(function() { deferred.resolve(data); }) .catch(function(error) { if (error.status) { error = { code: error.status, message: error.data }; } deferred.reject(error); }); return deferred.promise; }; }]); /* * Orange angular-swagger-ui - v0.6.5 * * (C) 2015 Orange, all right reserved * MIT Licensed */ 'use strict'; angular .module('swaggerUi') .service('swaggerModel', ["swaggerTranslator", function(swaggerTranslator) { var INLINE_MODEL_NAME = 'InlineModel'; /** * sample object cache to avoid generating the same one multiple times */ var sampleCache = {}; /** * retrieves object definition */ var resolveReference = this.resolveReference = function(openApiSpec, object) { var result = object; if (object.$ref) { var parts = object.$ref.replace('#/', '').split('/'); result = openApiSpec; for (var i = 0, j = parts.length; i < j; i++) { result = result[parts[i]]; } if (!result) { console.error('could not resolve model definition', object.$ref); } } result = resolveAllOf(openApiSpec, result); return angular.copy(result); }; /** * determines a property type */ var getType = this.getType = function(item) { var format = item.format; switch (format) { case 'int32': format = item.type; break; case 'int64': format = 'long'; break; } return format || item.type; }; /** * handles allOf property of a schema */ function resolveAllOf(openApiSpec, schema) { if (schema.allOf) { schema = angular.copy(schema); angular.forEach(schema.allOf, function(def) { var ref = resolveReference(openApiSpec, def); if (!def.$ref || !ref.discriminator) { // do not handle inherited properties here angular.merge(schema, ref); } }); delete schema.allOf; } return schema; } /** * generates a sample object (request body or response body) */ function getSampleObj(openApiSpec, schema, currentGenerated) { var sample, def, name, prop; currentGenerated = currentGenerated || {}; // used to handle circular references schema = resolveAllOf(openApiSpec, schema); if (schema.parentModelsRef) { angular.forEach(schema.parentModelsRef, function(ref) { var def = resolveReference(openApiSpec, ref); angular.merge(schema, def); }); } if (schema.default || schema.example) { sample = schema.default || schema.example; } else if (schema.properties) { sample = {}; for (name in schema.properties) { prop = schema.properties[name]; sample[name] = getSampleObj(openApiSpec, prop.schema || prop, currentGenerated); } } else if (schema.additionalProperties) { // this is a map/dictionary // @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#model-with-mapdictionary-properties def = resolveReference(openApiSpec, schema.additionalProperties); sample = { 'string': getSampleObj(openApiSpec, def, currentGenerated) }; } else if (schema.$ref) { // complex object def = resolveReference(openApiSpec, schema); if (def) { if (!sampleCache[schema.$ref] && !currentGenerated[schema.$ref]) { // object not in cache currentGenerated[schema.$ref] = true; sampleCache[schema.$ref] = getSampleObj(openApiSpec, def, currentGenerated); } sample = sampleCache[schema.$ref] || {}; } else { console.warn('AngularSwaggerUI: schema not found', schema.$ref); sample = schema.$ref; } } else if (schema.type === 'array') { sample = [getSampleObj(openApiSpec, schema.items, currentGenerated)]; } else if (schema.type === 'object') { sample = {}; } else { sample = schema.defaultValue || schema.example || getSampleValue(schema); } return sample; } /** * generates a sample value for a basic type */ function getSampleValue(schema) { var result, type = getType(schema); switch (type) { case 'long': case 'integer': result = 0; break; case 'boolean': result = false; break; case 'float': case 'double': case 'number': result = 0.0; break; case 'string': case 'byte': case 'binary': case 'password': result = 'string'; if (schema.enum && schema.enum.length > 0) { result = schema.enum[0]; } break; case 'date': result = (new Date()).toISOString().split('T')[0]; break; case 'date-time': result = (new Date()).toISOString(); break; } return result; } /** * generates a sample JSON string (request body or response body) */ this.generateSampleJson = function(openApiSpec, schema, example) { var obj, json; try { obj = example || getSampleObj(openApiSpec, schema); if (obj) { json = angular.toJson(obj, true); } } catch (ex) { console.error('AngularSwaggerUI: failed to generate sample json', schema, ex); json = 'failed to generate sample json'; } return json; }; /** * generates a sample XML string (request body or response body) */ this.generateSampleXml = function(openApiSpec, schema, example) { //TODO return '<?xml version="1.0" encoding="UTF-8"?>\n<!-- XML example cannot be generated -->'; }; /** * retrieves object class name based on $ref */ function getClassName(schema) { var parts = schema.$ref.split('/'); return parts[parts.length - 1]; } /** * inline model counter */ var countInLineModels = 1; /** * is model attribute required ? */ function isRequired(item, name) { return item.required && item.required.indexOf(name) !== -1; } /** * generates new inline model name */ function getInlineModelName() { return INLINE_MODEL_NAME + (countInLineModels++); } /** * identify models using inheritance */ this.resolveInheritance = function(openApiSpec) { angular.forEach(openApiSpec.definitions, function(schema, modelName) { resolveItemInheritance(openApiSpec, schema, schema, modelName); }); }; function resolveItemInheritance(openApiSpec, schema, def, modelName) { if (def.discriminator && !schema.subModelsRef) { schema.subModelsRef = []; angular.forEach(openApiSpec.definitions, function(subSchema, subModelName) { if (modelName !== subModelName && subSchema.allOf) { angular.forEach(subSchema.allOf, function(parent) { if (parent.$ref && modelName === getClassName(parent)) { subSchema.parentModelsRef = subSchema.parentModelsRef || []; subSchema.parentModelsRef.push({ '$ref': '#/definitions/' + modelName }); schema.subModelsRef.push({ '$ref': '#/definitions/' + subModelName }); } resolveItemInheritance(openApiSpec, subSchema, parent, subModelName); }); } }); } } /** * generate a model and its submodels from schema */ this.generateModel = function(openApiSpec, schema, operationId, showInheritedProperties) { var model = [], subModelIds = {}, subModels = {}, def; try { schema = resolveAllOf(openApiSpec, schema); // find models to generate if (schema.properties) { // if inline model subModels[getInlineModelName()] = schema; angular.merge(subModels, findAllModels(openApiSpec, schema, subModelIds)); } else { subModels = findAllModels(openApiSpec, schema, subModelIds); } def = resolveReference(openApiSpec, schema); if (!def.properties) { // if not complex type model.push('<strong>', getModelProperty(def, subModels, subModelIds, operationId), '</strong><br><br>'); } angular.forEach(subModels, function(subSchema, subModelName) { model.push(getModel(openApiSpec, subSchema, subModelName, subModels, subModelIds, operationId, showInheritedProperties)); }); } catch (ex) { console.error('AngularSwaggerUI: failed to generate model', schema, ex); model = ['failed to generate model']; } clearModelCache(); return model.join(''); }; /** * find all models to generate */ function findAllModels(openApiSpec, schema, subModelIds, modelName, onGoing) { var models = {}; if (modelName) { onGoing = onGoing || {}; // used to handle circular definitions if (onGoing[modelName]) { return models; } onGoing[modelName] = true; } schema = resolveAllOf(openApiSpec, schema); if (schema.properties) { angular.forEach(schema.properties, function(property) { inspectSubModel(openApiSpec, property, models, subModelIds, onGoing); }); } else if (schema.schema || schema.$ref) { var subSchema = schema.schema || schema, def = resolveReference(openApiSpec, subSchema), subPropertyModelName = getClassName(subSchema); if (def && (!def.type || def.type === 'object' || def.type === 'array')) { def = resolveAllOf(openApiSpec, def); models[subPropertyModelName] = def; subModelIds[subPropertyModelName] = countModel++; angular.merge(models, findAllModels(openApiSpec, def, subModelIds, subPropertyModelName, onGoing)); } } else if (schema.type === 'array') { inspectSubModel(openApiSpec, schema.items, models, subModelIds, onGoing); } else if (schema.additionalProperties) { // this is a map/dictionary inspectSubModel(openApiSpec, schema.additionalProperties, models, subModelIds, onGoing); } if (schema.subModelsRef) { // find missing subclasses var missingModelsRef = [], keys = Object.keys(subModelIds), i = 0; for (; i < schema.subModelsRef.length; i++) { if (keys.indexOf(getClassName(schema.subModelsRef[i])) === -1) { missingModelsRef.push(schema.subModelsRef[i]); } } if (missingModelsRef.length > 0) { // add sub classes addInheritanceModels(openApiSpec, missingModelsRef, models, subModelIds, onGoing); } } if (schema.parentModelsRef) { // find super classes addInheritanceModels(openApiSpec, schema.parentModelsRef, models, subModelIds, onGoing); } return models; } function addInheritanceModels(openApiSpec, inheritanceModels, models, subModelIds, onGoing) { angular.forEach(inheritanceModels, function(ref) { var def = resolveReference(openApiSpec, ref), subModelName = getClassName(ref); models[subModelName] = def; subModelIds[subModelName] = countModel++; angular.merge(models, findAllModels(openApiSpec, def, subModelIds, subModelName, onGoing)); }); } /** * look for submodels */ function inspectSubModel(openApiSpec, schema, models, subModelIds, onGoing) { var inlineModelName = generateInlineModel(schema, models, subModelIds); angular.merge(models, findAllModels(openApiSpec, schema, subModelIds, inlineModelName, onGoing)); } /** * generates an inline model if needed */ function generateInlineModel(subProperty, models, subModelIds) { var subModelName; if (subProperty.properties) { subModelName = getInlineModelName(); subProperty.modelName = subModelName; subModelIds[subModelName] = countModel++; models[subModelName] = subProperty; } return subModelName; } /** * generates an HTML link to a submodel */ function getSubModelLink(operationId, name) { var linkModelId = operationId + '-model-' + name; return ['<a class="model-link type" onclick="swaggerlink(\'', linkModelId, '\')">', name, '</a>'].join(''); } /** * model counter */ var countModel = 0; /** * generates a single model in HTML */ function getModel(openApiSpec, schema, modelName, subModels, subModelIds, operationId, showInheritedProperties) { var buffer = ['<div class="model" id="', operationId + '-model-' + modelName, '">']; schema = resolveAllOf(openApiSpec, schema); if (schema.properties) { buffer.push('<div><strong>', modelName); if (schema.parentModelsRef) { buffer.push('</strong> extends <strong>'); angular.forEach(schema.parentModelsRef, function(ref) { buffer.push(getSubModelLink(operationId, getClassName(ref)), ' '); if (!showInheritedProperties) { // remove inherited properties var parentSchema = resolveReference(openApiSpec, ref); angular.forEach(parentSchema.properties, function (property, name) { if (schema.properties[name]) { delete schema.properties[name]; } }); } }); buffer.pop(); } buffer.push(' {</strong></div>'); var hasProperties = false; angular.forEach(schema.properties, function(property, propertyName) { hasProperties = true; if (!property.type && property.$ref) { var ref = resolveReference(openApiSpec, property); if (ref.type !== 'object' && ref.type !== 'array') { // this is a simple type property = ref; } } buffer.push('<div class="pad"><strong>', propertyName, '</strong> (<span class="type">'); buffer.push(getModelProperty(property, subModels, subModelIds, operationId)); buffer.push('</span>'); if (!isRequired(schema, propertyName)) { buffer.push(', ', '<em>' + swaggerTranslator.translate('modelOptional') + '</em>'); } buffer.push(')'); if (property.description) { buffer.push(': ', property.description); } var enumValues = property.enum || (property.items && property.items.enum); if (enumValues) { buffer.push(', Enum = ', angular.toJson(enumValues).replace(/,/g, ', ')); } buffer.push(',</div>'); }); if (hasProperties) { buffer.pop(); buffer.push('</div>'); } buffer.push('<div><strong>}</strong></div>'); } else if (schema.type === 'array' || schema.additionalProperties) { buffer.push(getModelProperty(schema, subModels, subModelIds, operationId)); } else if (schema.type === 'object') { buffer.push('<strong>', modelName || getInlineModelName(), ' {<br>}</strong>'); } else if (schema.type) { buffer.push('<strong>', getType(schema), '</strong>'); } return buffer.join(''); } /** * retrieves model property class */ function getModelProperty(property, subModels, subModelIds, operationId) { var modelName, buffer = []; if (property.modelName) { buffer.push(getSubModelLink(operationId, property.modelName)); } else if (property.schema || property.$ref) { modelName = getClassName(property.schema || property); buffer.push(getSubModelLink(operationId, modelName)); } else if (property.type === 'array') { buffer.push('Array['); buffer.push(getModelProperty(property.items, subModels, subModelIds, operationId)); buffer.push(']'); } else if (property.properties) { buffer.push(getSubModelLink(operationId, modelName)); } else if (property.additionalProperties) { // this is a map/dictionary buffer.push('Map&lt;string, '); buffer.push(getModelProperty(property.additionalProperties, subModels, subModelIds, operationId)); buffer.push('&gt;'); } else { buffer.push(getType(property)); } return buffer.join(''); } /** * clears generated samples cache */ function clearModelCache() { countModel = 0; countInLineModels = 1; } this.clearCache = function() { sampleCache = {}; }; }]); /* * Orange angular-swagger-ui - v0.6.5 * * (C) 2015 Orange, all right reserved * MIT Licensed */ 'use strict'; angular .module('swaggerUi') .service('swaggerModules', ["$q", function($q) { var modules = {}; this.AUTH = 'AUTH'; this.BEFORE_LOAD = 'BEFORE_LOAD'; this.AFTER_LOAD = 'AFTER_LOAD'; this.BEFORE_PARSE = 'BEFORE_PARSE'; this.PARSE = 'PARSE'; this.BEFORE_DISPLAY = 'BEFORE_DISPLAY'; this.BEFORE_EXPLORER_LOAD = 'BEFORE_EXPLORER_LOAD'; this.AFTER_EXPLORER_LOAD = 'AFTER_EXPLORER_LOAD'; this.BEFORE_CONVERT = 'BEFORE_CONVERT'; /** * Adds a new module to swagger-ui */ this.add = function(phase, module, priority) { if (!modules[phase]) { modules[phase] = []; } if (!priority) { priority = 1; } module.swaggerModulePriority = priority; if (modules[phase].indexOf(module) < 0) { modules[phase].push(module); } modules[phase].sort(function(obj1, obj2) { if (obj1.swaggerModulePriority > obj2.swaggerModulePriority) { return -1; } else if (obj1.swaggerModulePriority < obj2.swaggerModulePriority) { return 1; } return 0; }); }; /** * Runs modules' "execute" function one by one */ function executeAll(deferred, phaseModules, args, phaseExecuted) { var module = phaseModules.shift(); if (module) { module .execute(args) .then(function(executed) { phaseExecuted = phaseExecuted || executed; executeAll(deferred, phaseModules, args, phaseExecuted); }) .catch(deferred.reject); } else { deferred.resolve(phaseExecuted); } } /** * Executes modules' phase */ this.execute = function(phase, args) { var deferred = $q.defer(), phaseModules = modules[phase] || []; if (!angular.isObject(args)) { console.warn('AngularSwaggerUI: module execution argument should be an object!'); } executeAll(deferred, [].concat(phaseModules), args); return deferred.promise; }; }]); /* * Orange angular-swagger-ui - v0.6.5 * * (C) 2015 Orange, all right reserved * MIT Licensed */ 'use strict'; angular .module('swaggerUi') .service('swaggerParser', ["$q", "$sce", "$location", "swaggerModel", "swaggerTranslator", function($q, $sce, $location, swaggerModel, swaggerTranslator) { var HTTP_METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'], trustedSources, operationId, paramId; /** * Module entry point */ this.execute = function(data) { var deferred = $q.defer(); if (data.openApiSpec && data.openApiSpec.swagger === '2.0' && (data.parser === 'json' || (data.parser === 'auto' && data.contentType === 'application/json'))) { trustedSources = data.trustedSources; try { parseSwagger2Json(deferred, data); } catch (e) { deferred.reject({ code: 500, message: swaggerTranslator.translate('errorParseFailed', e) }); } } else { deferred.resolve(false); } return deferred.promise; }; /** * parse swagger description to ease HTML generation */ function parseSwagger2Json(deferred, data) { var map = {}, form = {}, resources = [], infos = data.openApiSpec.info, openPath = $location.hash(), defaultContentType = 'application/json', sortResources = !data.openApiSpec.tags; operationId = 0; paramId = 0; parseInfos(data.openApiSpec, data.url, infos, defaultContentType); parseTags(data.openApiSpec, resources, map); parseOperations(data.openApiSpec, resources, form, map, defaultContentType, openPath); swaggerModel.resolveInheritance(data.openApiSpec); cleanUp(resources, openPath, sortResources); // prepare result data.ui = { infos: infos, form: form, resources: resources }; deferred.resolve(true); } /** * parse main infos */ function parseInfos(openApiSpec, url, infos, defaultContentType) { // build URL params var a = angular.element('<a href="' + url + '"></a>')[0]; openApiSpec.schemes = [openApiSpec.schemes && openApiSpec.schemes[0] || a.protocol.replace(':', '')]; openApiSpec.host = openApiSpec.host || a.host; openApiSpec.consumes = openApiSpec.consumes || [defaultContentType]; openApiSpec.produces = openApiSpec.produces || [defaultContentType]; // build main infos infos.scheme = openApiSpec.schemes[0]; infos.schemes = openApiSpec.schemes; infos.basePath = openApiSpec.basePath; infos.host = openApiSpec.host; infos.description = trustHtml(infos.description); infos.externalDocs = openApiSpec.externalDocs; if (infos.externalDocs) { infos.externalDocs.description = trustHtml(infos.externalDocs.description); } } /** * parse tags */ function parseTags(openApiSpec, resources, map) { var i, l, tag; if (!openApiSpec.tags) { resources.push({ name: 'default', open: true }); map['default'] = 0; } else { for (i = 0, l = openApiSpec.tags.length; i < l; i++) { tag = openApiSpec.tags[i]; resources.push(tag); map[tag.name] = i; } } } /** * parse operations */ function parseOperations(openApiSpec, resources, form, map, defaultContentType, openPath) { var i, path, pathObject, pathParameters, httpMethod, operation, tag, resource; for (path in openApiSpec.paths) { pathObject = openApiSpec.paths[path] = swaggerModel.resolveReference(openApiSpec, openApiSpec.paths[path]); pathParameters = pathObject.parameters || []; delete pathObject.parameters; for (httpMethod in pathObject) { if (HTTP_METHODS.indexOf(httpMethod) >= 0) { operation = pathObject[httpMethod]; operation.description = trustHtml(operation.description); operation.produces = operation.produces || openApiSpec.produces; form[operationId] = { responseType: operation.produces && operation.produces[0] || defaultContentType }; operation.httpMethod = httpMethod; operation.path = path; operation.security = operation.security || openApiSpec.security; parseParameters(openApiSpec, operation, pathParameters, form, defaultContentType, openPath); parseResponses(openApiSpec, operation, openPath); operation.tags = operation.tags || ['default']; // map operation to resources for (i = 0; i < operation.tags.length; i++) { tag = operation.tags[i]; if (typeof map[tag] === 'undefined') { map[tag] = resources.length; resources.push({ name: tag }); } resource = resources[map[tag]]; resource.operations = resource.operations || []; operation.id = operationId++; operation.operationId = operation.operationId || ('operation-' + operation.id); operation.open = openPath && (openPath.match(new RegExp(operation.operationId + '$|' + operation.operationId + '-(default|parameter|response)-model-.*|' + resource.name + '\\*$'))); resource.operations.push(angular.copy(operation)); if (operation.open) { resource.open = true; } } } } } } /** * compute path and operation parameters */ function computeParameters(openApiSpec, pathParameters, operation) { var i, j, k, l, operationParameters = operation.parameters || [], parameters = [].concat(operationParameters), found, pathParameter, operationParameter; for (i = 0, l = pathParameters.length; i < l; i++) { found = false; pathParameter = swaggerModel.resolveReference(openApiSpec, pathParameters[i]); for (j = 0, k = operationParameters.length; j < k; j++) { operationParameter = swaggerModel.resolveReference(openApiSpec, operationParameters[j]); if (pathParameter.name === operationParameter.name && pathParameter.in === operationParameter.in) { // overriden parameter found = true; break; } } if (!found) { // add path parameter to operation ones parameters.push(pathParameter); } } return parameters; } /** * parse operation parameters */ function parseParameters(openApiSpec, operation, pathParameters, form, defaultContentType, openPath) { var i, l, param, openModel, parameters = operation.parameters = computeParameters(openApiSpec, pathParameters, operation); for (i = 0, l = parameters.length; i < l; i++) { //TODO manage 'collectionFormat' (csv, multi etc.) ? //TODO manage constraints (pattern, min, max etc.) ? param = parameters[i] = swaggerModel.resolveReference(openApiSpec, parameters[i]); param.id = paramId; param.type = swaggerModel.getType(param); param.description = trustHtml(param.description); if (param.items && param.items.enum) { param.enum = param.items.enum; param.default = param.items.default; } param.subtype = param.enum ? 'enum' : param.type; // put param into form scope form[operationId][param.name] = param.default || ''; if (param.schema) { openModel = openPath && openPath.match(new RegExp(operation.operationId + '-parameter-model-.*')); param.schema.display = openModel ? 0 : 1; } // fix consumes if (param.in === 'body') { operation.consumes = operation.consumes || openApiSpec.consumes || [defaultContentType]; form[operationId].contentType = operation.consumes && operation.consumes[0]; } else if (param.in === 'formData') { operation.consumes = operation.consumes || [param.subtype === 'file' ? 'multipart/form-data' : 'application/x-www-form-urlencoded']; form[operationId].contentType = operation.consumes && operation.consumes[0]; } if (param.schema && operation.consumes && operation.consumes.indexOf('application/xml') >= 0) { param.schema.xml = swaggerModel.generateSampleXml(openApiSpec, param.schema); } paramId++; } } /** * parse operation responses */ function parseResponses(openApiSpec, operation, openPath) { var code, response, openModel; if (operation.responses) { for (code in operation.responses) { response = operation.responses[code] = swaggerModel.resolveReference(openApiSpec, operation.responses[code]); response.description = trustHtml(response.description); if (response.schema) { // if (response.examples && response.examples[operation.produces[0]]) { // sampleJson = angular.toJson(response.examples[operation.produces[0]], true); // } else { // sampleJson = swaggerModel.generateSampleJson(openApiSpec, response.schema); // } // response.schema.json = sampleJson; // if (operation.produces && operation.produces.indexOf('application/xml') >= 0) { // response.schema.xml = swaggerModel.generateSampleXml(openApiSpec, response.schema); // } // model = swaggerModel.generateModel(openApiSpec, response.schema, operation.operationId); if (code === '200' || code === '201') { operation.responseClass = response; openModel = openPath && openPath.match(new RegExp(operation.operationId + '-default-model-.*')); operation.responseClass.display = openModel ? 0 : 1; operation.responseClass.status = code; parseHeaders(openApiSpec, operation, response); delete operation.responses[code]; } else { openModel = openPath && openPath.match(new RegExp(operation.operationId + '-response-model-.*')); response.display = openModel ? 0 : 1; operation.hasResponses = true; } } else { operation.hasResponses = true; } } } } /** * parse operation response headers */ function parseHeaders(openApiSpec, operation, response) { if (response.headers) { operation.headers = response.headers; for (var name in operation.headers) { var header = operation.headers[name]; header.type = swaggerModel.getType(header); if (header.type === 'array') { header.type = 'Array[' + swaggerModel.getType(header.items) + ']'; } header.description = trustHtml(header.description); } delete response.headers; } } function cleanUp(resources, openPath, sortResources) { var i, resource, operations; for (i = 0; i < resources.length; i++) { resource = resources[i]; operations = resources[i].operations; resource.open = resource.open || openPath === resource.name || openPath === resource.name + '*'; if (!operations || (operations && operations.length === 0)) { resources.splice(i--, 1); } } if (sortResources) { // sort resources alphabeticaly resources.sort(function(a, b) { if (a.name > b.name) { return 1; } else if (a.name < b.name) { return -1; } return 0; }); } } function trustHtml(text) { var trusted = text; if (typeof text === 'string' && trustedSources) { trusted = $sce.trustAsHtml(escapeChars(text)); } // else ngSanitize MUST be added to app return trusted; } function escapeChars(text) { return text .replace(/&/g, '&amp;') .replace(/<([^\/a-zA-Z])/g, '&lt;$1') .replace(/"/g, '&quot;') .replace(/'/g, '&#039;'); } }]) .run(["swaggerModules", "swaggerParser", function(swaggerModules, swaggerParser) { swaggerModules.add(swaggerModules.PARSE, swaggerParser, 1); }]); /* * Orange angular-swagger-ui - v0.6.5 * * (C) 2015 Orange, all right reserved * MIT Licensed */ 'use strict'; angular .module('swaggerUi') .config(["swaggerTranslatorProvider", function(swaggerTranslatorProvider) { swaggerTranslatorProvider .addTranslations('en', { infoContactCreatedBy: 'Created by {{name}}', infoContactUrl: 'See more at', infoContactEmail: 'Contact the developer', infoLicense: 'License: ', infoBaseUrl: 'BASE URL', infoApiVersion: 'API VERSION', infoHost: 'HOST', endPointToggleOperations: 'Open/Hide', endPointListOperations: 'List operations', endPointExpandOperations: 'Expand operations', operationDeprected: 'Warning: Deprecated', operationImplementationNotes: 'Implementation notes', externalDocs: 'External docs', headers: 'Response headers', headerName: 'Header', headerDescription: 'Description', headerType: 'Type', parameters: 'Parameters', parameterName: 'Parameter', parameterValue: 'Value', parameterDescription: 'Description', parameterType: 'Parameter type', parameterDataType: 'Data type', parameterOr: ' or ', parameterRequired: '(required)', parameterModel: 'Model', parameterSchema: 'Example value', parameterContentType: 'Parameter content type', parameterDefault: '{{default}} (default)', parameterSetValue: 'Click to set as parameter value', responseClass: 'Response class (status {{status}})', responseModel: 'Model', responseSchema: 'Example value', responseContentType: 'Response content type', responses: 'Response messages', responseCode: 'HTTP status code', responseReason: 'Reason', responseHide: 'Hide response', modelOptional: 'optional', modelOr: ' or ', explorerUrl: 'Request URL', explorerBody: 'Response body', explorerCode: 'Response code', explorerHeaders: 'Response headers', explorerLoading: 'Loading...', explorerTryIt: 'Try it out!', errorNoParserFound: 'No parser found for OpenApi specification of type {{type}}', errorParseFailed: 'Failed to parse OpenApi specification: {{message}}', errorJsonParse: 'Failed to parse JSON', errorNoYamlParser: 'No YAML parser found, please make sure to include js-yaml library', authRequired: 'Authorization required', authAvailable: 'Available authorizations', apiKey: 'API key authorization', authParamName: 'Name', authParamType: 'In', authParamValue: 'Value', basic: 'Basic authorization', authLogin: 'Login', authPassword: 'Password', oauth2: 'oAuth2 authorization', authOAuthDesc: 'Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes. API requires the following scopes. Select which ones you want to grant.', authAuthorizationUrl: 'Authorization URL', authFlow: 'Flow', authTokenUrl: 'Token URL', authScopes: 'Scopes', authDone: 'Done', authAuthorize: 'Authorize', authClientId: 'Client ID', authClientSecret: 'Client secret', authLogout: 'Logout', authLogged: 'You\'re currently logged in' }); }]); angular.module('swaggerUi').run(['$templateCache', function($templateCache) { $templateCache.put('templates/endpoint.html', '<div id="{{api.name}}" class="clearfix" ng-class="api.css"> <ul id="{{api.name}}*" class="list-inline pull-left endpoint-heading"> <li> <h4> <a ng-click="api.open=!api.open;api.open?permalink(api.name):\'\';" ng-bind="api.name"></a> <span ng-if="api.description"> : <span n