diff --git a/lib/resource/Directory.js b/lib/resource/Directory.js index 3e456f6e..9afeb1b4 100644 --- a/lib/resource/Directory.js +++ b/lib/resource/Directory.js @@ -76,6 +76,22 @@ Directory.prototype.getAccountCreationPolicy = function getAccountCreationPolicy return this.dataStore.getResource(this.accountCreationPolicy.href, args.options, require('./InstanceResource'), args.callback); }; +/** + * Get the {@link Schema} resource of this Directory resource. The schema allows + * you to control which attributes are required when accounts are created in this + * directory. + * + * @param {ExpansionOptions} [expansionOptions] + * For retrieving linked resources of the {@link Schema} during this request. + * + * @param {Function} callback + * Callback function, will be called with (err, {@link Schema}). + */ +Directory.prototype.getAccountSchema = function getAccountSchema(/* [options,] callback */) { + var args = utils.resolveArgs(arguments, ['options', 'callback'], true); + return this.dataStore.getResource(this.accountSchema.href, args.options, require('./Schema'), args.callback); +}; + /** * Get the {@link PasswordPolicy} resource of this Directory resource. * diff --git a/lib/resource/Field.js b/lib/resource/Field.js new file mode 100644 index 00000000..d29cc3d2 --- /dev/null +++ b/lib/resource/Field.js @@ -0,0 +1,33 @@ +'use strict'; + +var Resource = require('./Resource'); +var utils = require('../utils'); + +/** + * @class Field + * + * @description + * Encapsulates an account field, as part of a {@link Schema}. + * + * This class should not be manually constructed. It should be obtained from one of these methods: + * + * - {@link Schema#getFields Schema.getFields()}. + */ +function Field(){ + Field.super_.apply(this, arguments); +} + +utils.inherits(Field, Resource); + +/** + * Save changes to this resource. + * + * @param {Function} callback + * The function to call when the save operation is complete. Will be called + * with the parameters (err, updatedResource). + */ +Field.prototype.save = function save(callback) { + this.dataStore.saveResource(this, callback); +}; + +module.exports = Field; \ No newline at end of file diff --git a/lib/resource/Schema.js b/lib/resource/Schema.js new file mode 100644 index 00000000..021865c5 --- /dev/null +++ b/lib/resource/Schema.js @@ -0,0 +1,53 @@ +'use strict'; + +var Resource = require('./Resource'); +var utils = require('../utils'); + +/** + * @class Schema + * + * @description + * Encapsulates the Schema resource of a {@link Directory}. This schema allows you + * to control which Account attributes (referred to as fields) are required when + * creating new accounts in the directory. For full documentation of this resource, please see + * [How to Manage an Account’s Required Attributes](https://docs.stormpath.com/rest/product-guide/latest/accnt_mgmt.html#how-to-manage-an-account-s-required-attributes). + * + * This class should not be manually constructed. It should be obtained from one of these methods: + * - {@link Directory#getAccountSchema Directory.getAccountSchema()}. + * + * @example Disabling a field requirement. + * var _ = require('lodash'); + * + * schema.getFields(function (err, fieldsCollection) { + * var givenNameField = _.find(fieldsCollection.items, { + * name: 'givenName' + * }); + * + * givenNameField.required = false; + * + * givenNameField.save(); + * }); + */ +function Schema() { + Schema.super_.apply(this, arguments); +} + +utils.inherits(Schema, Resource); + +/** + * Get the collection of {@link Field Fields} for this schema. + * + * @param {CollectionQueryOptions} [options] + * Options for querying, paginating, and expanding the collection. + * + * @param {Function} callback + * The function to call when the operation is complete. Will be called + * with the parameters (err, {@link CollectionResource}). The collection will + * be a list of {@link Field} objects. + */ +Schema.prototype.getFields = function () { + var args = utils.resolveArgs(arguments, ['options', 'callback'], true); + return this.dataStore.getResource(this.fields.href, args.options, require('./Field'), args.callback); +}; + +module.exports = Schema; \ No newline at end of file diff --git a/test/sp.resource.directory_test.js b/test/sp.resource.directory_test.js index 72a507f6..a9151476 100644 --- a/test/sp.resource.directory_test.js +++ b/test/sp.resource.directory_test.js @@ -9,6 +9,7 @@ var Account = require('../lib/resource/Account'); var Group = require('../lib/resource/Group'); var Organization = require('../lib/resource/Organization'); var OrganizationAccountStoreMapping = require('../lib/resource/OrganizationAccountStoreMapping'); +var Schema = require('../lib/resource/Schema'); var Tenant = require('../lib/resource/Tenant'); var Provider = require('../lib/resource/Provider'); var Directory = require('../lib/resource/Directory'); @@ -380,6 +381,49 @@ describe('Resources: ', function () { }); }); + describe('get account schema', function () { + describe('if accountSchema href is set', function () { + var opt; + var getResourceStub; + var sandbox; + var app; + var directory; + var cbSpy; + + before(function () { + opt = {}; + sandbox = sinon.sandbox.create(); + app = { accountSchema: { href: 'boom!' } }; + directory = new Directory(app, dataStore); + cbSpy = sandbox.spy(); + + getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) { + cb(); + }); + + directory.getAccountSchema(cbSpy); + directory.getAccountSchema(opt, cbSpy); + }); + + after(function () { + sandbox.restore(); + }); + + it('should get account schema', function () { + cbSpy.should.have.been.calledTwice; + getResourceStub.should.have.been.calledTwice; + + getResourceStub.should.have.been.calledWith( + app.accountSchema.href, null, Schema, cbSpy + ); + + getResourceStub.should.have.been.calledWith( + app.accountSchema.href, opt, Schema, cbSpy + ); + }); + }); + }); + describe('get organization mappings', function () { describe('if organizationMappings href are set', function () { var opt; diff --git a/test/sp.resource.field_test.js b/test/sp.resource.field_test.js new file mode 100644 index 00000000..b424b15c --- /dev/null +++ b/test/sp.resource.field_test.js @@ -0,0 +1,47 @@ +'use strict'; + +var common = require('./common'); +var sinon = common.sinon; + +var DataStore = require('../lib/ds/DataStore'); +var Field = require('../lib/resource/Field'); + +describe('Field Resource', function () { + describe('save()', function () { + var sandbox; + var field; + var dataStore; + var requestExecutorStub; + + var mockField = { + href: 'https://api.stormpath.com/v1/fields/7dDfMOkrekkLhbWBLcGWuN', + createdAt: '2016-08-02T20:16:21.931Z', + modifiedAt: '2016-08-02T20:16:21.931Z', + name: 'givenName', + required: true, + schema: { + href: 'https://api.stormpath.com/v1/schemas/7dDfMLQmkARN4mQK9MPGIJ' + } + }; + + before(function () { + dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); + sandbox = sinon.sandbox.create(); + field = new Field(mockField, dataStore); + requestExecutorStub = sandbox.stub(dataStore.requestExecutor, 'execute'); + }); + + after(function () { + sandbox.restore(); + }); + + it('should post the resource to the REST API', function () { + field.save(); + requestExecutorStub.should.have.been.calledWith({ + body: mockField, + uri: mockField.href, + method: 'POST' + }); + }); + }); +}); \ No newline at end of file diff --git a/test/sp.resource.schema_test.js b/test/sp.resource.schema_test.js new file mode 100644 index 00000000..52933cf8 --- /dev/null +++ b/test/sp.resource.schema_test.js @@ -0,0 +1,69 @@ +'use strict'; + +var common = require('./common'); +var assert = common.assert; +var sinon = common.sinon; + +var DataStore = require('../lib/ds/DataStore'); +var Field = require('../lib/resource/Field'); +var Schema = require('../lib/resource/Schema'); + +describe('Schema Resource', function(){ + + describe('getFields()', function(){ + + var sandbox; + var schema; + var dataStore; + + var mockSchema = { + href: 'https://api.stormpath.com/v1/schemas/7cvoYkLuGzpnAuVlKQdiDf/', + fields: { + href: 'https://api.stormpath.com/v1/schemas/7cvoYkLuGzpnAuVlKQdiDf/fields' + } + }; + + var mockFieldsResponse = { + href: 'https://api.stormpath.com/v1/schemas/7dDfMLQmkARN4mQK9MPGIJ/fields', + offset: 0, + limit: 25, + size: 2, + items: [ + { + href: 'https://api.stormpath.com/v1/fields/7dDfMOkrekkLhbWBLcGWuN', + createdAt: '2016-08-02T20:16:21.931Z', + modifiedAt: '2016-08-02T20:16:21.931Z', + name: 'givenName', + required: true, + schema: { + href: 'https://api.stormpath.com/v1/schemas/7dDfMLQmkARN4mQK9MPGIJ' + } + }, + ] + }; + + + + before(function(){ + dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); + sandbox = sinon.sandbox.create(); + schema = new Schema(mockSchema, dataStore); + + sandbox.stub(dataStore.requestExecutor, 'execute', function (req, cb) { + cb(null, mockFieldsResponse); + }); + }); + + after(function(){ + sandbox.restore(); + }); + + it('should return Field instances', function(done){ + schema.getFields(function(err, result){ + assert.equal(result.items, mockFieldsResponse.items); + assert.instanceOf(mockFieldsResponse.items[0], Field); + done(); + }); + }); + }); +}); \ No newline at end of file