├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── assets ├── sp.png └── sp_error.png ├── index.js ├── package-lock.json ├── package.json └── test └── integration ├── config.sample.js ├── files └── spsave.txt ├── spaddin.key └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | test/integration/config.js 3 | test/performance/files/SiteAssets/** 4 | test/performance/output/** 5 | test/performance/config.js -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run mocha", 6 | "type": "node", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", 9 | "stopOnEntry": false, 10 | "args": ["${workspaceRoot}\\test\\integration\\test.js"], 11 | "cwd": "${workspaceRoot}", 12 | "preLaunchTask": null, 13 | "runtimeExecutable": null, 14 | "runtimeArgs": [ 15 | "--nolazy" 16 | ], 17 | "env": { 18 | "NODE_ENV": "development" 19 | }, 20 | "externalConsole": false, 21 | "sourceMaps": false, 22 | "outDir": null 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.renderWhitespace": true, 3 | "editor.tabSize": 2, 4 | "files.trimTrailingWhitespace": false, 5 | "files.exclude": { 6 | "node_modules/": false, 7 | "**/*.js": { 8 | "when": "$(basename).ts" 9 | }, 10 | "**/*.d.ts": { 11 | "when": "$(basename).js" 12 | }, 13 | "**/*.js.map": true 14 | }, 15 | "editor.wrappingColumn": 5000, 16 | "files.autoSave": "afterDelay", 17 | "files.autoSaveDelay": 1000 18 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sergei Sergeev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gulp-spsave 2 | 3 | [![NPM](https://nodei.co/npm/gulp-spsave.png?mini=true)](https://nodei.co/npm/gulp-spsave/) 4 | [![npm version](https://badge.fury.io/js/gulp-spsave.svg)](https://badge.fury.io/js/gulp-spsave) 5 | 6 | ### Need help on SharePoint with Node.JS? Join our gitter chat and ask question! [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/sharepoint-node/Lobby) 7 | 8 | Gulp plugin for [spsave](https://github.com/s-KaiNet/spsave) - save files in SharePoint using node.js easily. 9 | 10 | ---------- 11 | 12 | ## How to use: 13 | #### Install: 14 | ```bash 15 | npm install gulp-spsave --save-dev 16 | ``` 17 | #### Usage: 18 | 19 | ```javascript 20 | var spsave = require('gulp-spsave'); 21 | var gulp = require('gulp'); 22 | 23 | gulp.task("default", function(){ 24 | return gulp.src("./build/*.js") 25 | .pipe(spsave(coreOptions, creds)); 26 | }); 27 | ``` 28 | 29 | ## Options: 30 | 31 | Exactly the same as for [spsave](https://github.com/s-KaiNet/spsave), except file content options (because the file is piped through the gulp stream). 32 | That means no need to provide such options as `fileName`, `fileContent`, `glob`, `file`, `base` (`base` can be provided for the `gulp.src`, see samples below). 33 | I recommend you look at the [spsave](https://github.com/s-KaiNet/spsave) page to get a better understanding. 34 | 35 | #### Core options (passed to `spsave`): 36 | The same as for [spsave core options](https://github.com/s-KaiNet/spsave#core-options) plus two additional options exclusive to `gulp-spsave`: 37 | - `folder` - required string, SharePoint folder to upload file to (can be the url to document library) 38 | - `flatten` - boolean, default true, when true all files will be uploaded to `folder` provided, regardles of the file physical location. For example, if folder equal to `MyAppAssets` and you pipe two files `app/controllers/HomeCtrl.js` and `app/templates/home.html`, then `MyAppAssets` will contain both `HomeCtrl.js` and `home.html` in the root. 39 | If `flatten` is false, `gulp-spsave` will look for base for the file and will use this base for upload file in a particular folder (or create this folder automatically if required). See [gulp API docs](https://github.com/gulpjs/gulp/blob/master/docs/API.md), `gulp.src(globs[, options])` and [glob2base](https://github.com/contra/glob2base). 40 | 41 | #### Credentials: 42 | 43 | `gulp-spsave` implicitly depends on another module used for SharePoint authentication from node js - [node-sp-auth](https://github.com/s-KaiNet/node-sp-auth). For credentials param you need to pass exactly the same object, as for `node-sp-auth` [credentialsOptions object](https://github.com/s-KaiNet/node-sp-auth#params). That also means that `gulp-spsave` supports all authentication options supported by `node-sp-auth`. See examples below for more info. 44 | You can also pass a `null` as credentials, in that case `gulp-spsave` will ask you for credentials and will store your credentials in a user folder in an encrypted manner (everything is handled by `node-sp-auth` actually). 45 | 46 | Examples: 47 | -- 48 | 49 | Imagine we have `settings.js` which stores all sensitive information for us (credential information, client id\client secret etc.): 50 | 51 | ```javascript 52 | module.exports = { 53 | username: "[user]", 54 | password: "[pass]" 55 | } 56 | ``` 57 | 58 | 1.Watch for file changes in scripts, then bundle, minify, whatever, and upload to SharePoint automatically: 59 | 60 | ---------- 61 | 62 | 63 | ```javascript 64 | //sensitive data stored in external file: 65 | var creds = require("./settings.js"); 66 | gulp.task("buildJS", function(){ 67 | return gulp.src("./Scripts/**/*.js") 68 | .pipe(concat()) 69 | .pipe(uglify()) 70 | .pipe(gulp.dest("./build")); 71 | }); 72 | 73 | gulp.task("copyToSharePoint", ["buildJS"], function(){ 74 | return gulp.src("./build/*.js") 75 | .pipe(spsave({ 76 | siteUrl: settings.siteUrl, 77 | folder: "YourAppAssets/js" 78 | }, creds)); 79 | }); 80 | 81 | gulp.task("watch", function(){ 82 | gulp.watch(["./Scripts/**/*.js"], ["copyToSharePoint"]); 83 | }); 84 | ``` 85 | 2.Save all files from `App/build` to SharePoint: 86 | 87 | ---------- 88 | 89 | ```javascript 90 | //sensitive data stored in external file: 91 | var creds = require("./settings.js"); 92 | gulp.task("spsave", function () { 93 | return gulp.src(["App/build/*.*"]) 94 | .pipe($.spsave({ 95 | siteUrl: settings.siteUrl, 96 | folder: "App/build", 97 | flatten: true 98 | }, creds)); 99 | }); 100 | ``` 101 | 3.Watch all javascript file changes in `ng` (stands for angular) folder and upload that file automatically in SharePoint with preserved folder structure: 102 | 103 | ---------- 104 | 105 | 106 | ```javascript 107 | //sensitive data stored in external file: 108 | var creds = require("./settings.js"); 109 | gulp.watch("App/ng/**/*.js", function (event) { 110 | gulp.src(event.path) 111 | .pipe($.spsave({ 112 | siteUrl: settings.siteUrl, 113 | folder: "AppAssets", 114 | flatten: false 115 | }, creds)); 116 | }); 117 | ``` 118 | In this sample `base` will be equal to `App/ng`. If file path is `App/ng/controllers/HomeCtrl.js`, then it will saved under `AppAssets/controllers/HomeCtrl.js` (if some folders are missing, they will be created by `spsave` automatically). Next sample demonstrate how can you save it under `AppAssets/ng/controllers/HomeCtrl.js` 119 | 120 | 4.You can also explicitly provide `base` for `gulp.src`: 121 | 122 | ---------- 123 | 124 | ```javascript 125 | //sensitive data stored in external file: 126 | var creds = require("./settings.js"); 127 | gulp.watch("App/ng/**/*.js", function (event) { 128 | gulp.src(event.path, { base: "App" }) 129 | .pipe($.spsave({ 130 | siteUrl: settings.siteUrl, 131 | folder: "AppAssets", 132 | flatten: false 133 | }, creds)); 134 | }); 135 | ``` 136 | In this case file be saved under `AppAssets/ng/controllers/HomeCtrl.js` path. 137 | 138 | 5.Upload search display template with metadata: 139 | 140 | ---------- 141 | 142 | ```javascript 143 | //sensitive data stored in external file: 144 | var creds = require("./settings.js"); 145 | gulp.watch("App/search/Item_Display.js", function (event) { 146 | gulp.src(event.path) 147 | .pipe($.spsave({ 148 | siteUrl: settings.siteUrl, 149 | folder: "_catalogs/masterpage/Display Templates/Search", 150 | flatten: true, 151 | filesMetaData: [{ 152 | fileName: "Item_Display.js", 153 | metadata: { 154 | "__metadata": { type: "SP.Data.OData__x005f_catalogs_x002f_masterpageItem" }, 155 | Title: "SPSave Display Template", 156 | DisplayTemplateLevel: "Item", 157 | TargetControlType: { 158 | "__metadata": { 159 | "type": "Collection(Edm.String)" 160 | }, 161 | "results": [ 162 | "SearchResults" 163 | ] 164 | }, 165 | ManagedPropertyMapping: `'Title':'Title','Path':'Path','Description':'Description'`, 166 | ContentTypeId: "0x0101002039C03B61C64EC4A04F5361F38510660500A0383064C59087438E649B7323C95AF6", 167 | TemplateHidden: false 168 | } 169 | }] 170 | }, creds)); 171 | }); 172 | ``` 173 | ...and any other scenarios you need. 174 | 175 | For list of all options for the `spsave` refer to the [git hub repository](https://github.com/s-KaiNet/spsave). 176 | 177 | ## Integration testing: 178 | 1. Rename file `/test/integration/config.sample.js` to `config.js`. 179 | 2. Update information in `config.js` with appropriate values (urls, credentials, environment). 180 | 3. Run `npm run test-int`. 181 | 182 | Known Issues 183 | -- 184 | 185 | When heavily utilizing watchers along with `gulp-spsave` you may see errors "Save conflict" or "Cobalt error". [spsave](https://github.com/s-KaiNet/spsave) tries to recover from these errors by trying to re-upload the file once or twice again. But usually it's a good idea to use [gulp-plumber](https://github.com/floatdrop/gulp-plumber) or similar tool in order to make sure that your watchers will not be broken when errors occur. 186 | Normally you can do the following in your `gulpfile.js`: 187 | 188 | ```javascript 189 | var plumber = require("gulp-plumber"); 190 | var onError = function (err) { 191 | console.log(err); 192 | this.emit("end"); 193 | }; 194 | gulp.watch(["App/index.html"], function (event) { 195 | return gulp.src(event.path, { base: "App" }) 196 | .pipe(plumber({ 197 | errorHandler: onError 198 | })) 199 | .pipe(spsave(settings)); 200 | }); 201 | 202 | ``` 203 | 204 | In case of error, your watch will remain up and running regardless of the error. 205 | -------------------------------------------------------------------------------- /assets/sp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/gulp-spsave/caac23d9ed128fd61bc13da86afc9be2ffbdd666/assets/sp.png -------------------------------------------------------------------------------- /assets/sp_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/gulp-spsave/caac23d9ed128fd61bc13da86afc9be2ffbdd666/assets/sp_error.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var spsave = require('spsave').spsave, 2 | PluginError = require('plugin-error'), 3 | path = require("path"), 4 | through = require("through2"), 5 | defaults = require('lodash.defaults'), 6 | assign = require('lodash.assign'), 7 | notifier = require('node-notifier'); 8 | 9 | var PLUGIN_NAME = 'gulp-spsave'; 10 | 11 | function gulpspsave(coreOptions, creds) { 12 | if (!coreOptions) { 13 | throw new PluginError(PLUGIN_NAME, 'Missing parameters'); 14 | } 15 | 16 | var files = []; 17 | var newOptions = defaults(assign({}, coreOptions), { 18 | flatten: true 19 | }); 20 | 21 | var notify = coreOptions.notification; 22 | newOptions.notification = false; 23 | 24 | function uploadFile(file, enc, cb) { 25 | if (file.isNull()) { 26 | cb(null, file); 27 | return; 28 | } 29 | 30 | if (file.isStream()) { 31 | cb(new PluginError(PLUGIN_NAME, 'Streaming not supported')); 32 | return; 33 | } 34 | 35 | if (file.isBuffer()) { 36 | var oldBase = file.base; 37 | if (newOptions.flatten) { 38 | file.base = null; 39 | } 40 | 41 | var fileName = path.basename(file.path); 42 | files.push(fileName); 43 | spsave(newOptions, creds, { 44 | file: file, 45 | folder: newOptions.folder 46 | }) 47 | .then(function () { 48 | file.base = oldBase; 49 | cb(null, file); 50 | return null; 51 | }) 52 | .catch(function (err) { 53 | if (notify) { 54 | notifier.notify({ 55 | title: `spsave: ${fileName}: error occured`, 56 | message: 'For details see console log', 57 | icon: path.join(__dirname, 'assets/sp_error.png') 58 | }); 59 | } 60 | cb(new PluginError(PLUGIN_NAME, err, { showStack: true })); 61 | return; 62 | }); 63 | } 64 | } 65 | 66 | function endStream(cb) { 67 | var showNotification = function () { 68 | notifier.notify({ 69 | title: `spsave: ${files.length} file(s) uploaded`, 70 | message: files.join(', '), 71 | icon: path.join(__dirname, 'assets/sp.png') 72 | }, function (err) { 73 | if (err) { 74 | cb(new PluginError(PLUGIN_NAME, err, { showStack: true })); 75 | return; 76 | } 77 | 78 | cb(); 79 | }); 80 | 81 | }; 82 | 83 | if (files.length > 0 && notify) { 84 | try { 85 | showNotification(); 86 | } catch (err) { 87 | cb(new PluginError(PLUGIN_NAME, err, { showStack: true })); 88 | } 89 | } else { 90 | cb(); 91 | } 92 | } 93 | 94 | return through.obj(uploadFile, endStream); 95 | } 96 | 97 | module.exports = gulpspsave; 98 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-spsave", 3 | "description": "Gulp pluging for saving files in SharePoint.", 4 | "version": "4.0.0", 5 | "author": "Sergei Sergeev (https://github.com/s-KaiNet)", 6 | "engines": { 7 | "node": ">=4.0.0" 8 | }, 9 | "scripts": { 10 | "test-int": "mocha ./test/integration/test.js" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/s-KaiNet/gulp-spsave/issues" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/s-KaiNet/gulp-spsave.git" 18 | }, 19 | "main": "./index.js", 20 | "licenses": [ 21 | { 22 | "type": "MIT", 23 | "url": "http://www.opensource.org/licenses/mit-license.php" 24 | } 25 | ], 26 | "dependencies": { 27 | "lodash.assign": "^4.2.0", 28 | "lodash.defaults": "^4.2.0", 29 | "node-notifier": "^7.0.2", 30 | "path": "^0.12.7", 31 | "plugin-error": "^1.0.1", 32 | "spsave": "^4.0.0", 33 | "through2": "4.0.2" 34 | }, 35 | "devDependencies": { 36 | "chai": "^4.2.0", 37 | "console.table": "^0.10.0", 38 | "gulp": "^3.9.1", 39 | "mocha": "^8.0.1", 40 | "sinon": "^9.0.2", 41 | "sp-request": "^3.0.0", 42 | "stream-assert": "^2.0.3" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/integration/config.sample.js: -------------------------------------------------------------------------------- 1 | exports.onlineUrl = 'https://[tenant].sharepoint.com/'; 2 | exports.onpremAdfsEnabledUrl = '[sharepint on premise url with adfs configured]'; 3 | exports.onpremNtlmEnabledUrl = '[sharepint on premise url with ntlm]'; 4 | 5 | exports.onlineCreds = { 6 | username: '[username]', 7 | password: '[password]' 8 | }; 9 | 10 | exports.onpremCreds = { 11 | username: '[username]', 12 | password: '[password]', 13 | domain: 'sp' 14 | }; 15 | 16 | exports.onpremAddinOnly = { 17 | clientId: '[clientId]', 18 | issuerId: '[issuerId]', 19 | realm: '[realm]', 20 | rsaPrivateKeyPath: '[rsaPrivateKeyPath]', 21 | shaThumbprint: '[shaThumbprint]' 22 | }; 23 | 24 | exports.onlineAddinOnly = { 25 | clientId: '[clientId]', 26 | clientSecret: '[clientSecret]', 27 | realm: '[realm]' 28 | }; 29 | 30 | exports.adfsCredentials = { 31 | username: '[username]', 32 | password: '[password]', 33 | relyingParty: '[relying party]', 34 | adfsUrl: '[adfs url]' 35 | }; 36 | -------------------------------------------------------------------------------- /test/integration/files/spsave.txt: -------------------------------------------------------------------------------- 1 | spsave test file -------------------------------------------------------------------------------- /test/integration/spaddin.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /test/integration/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'development'; 2 | 3 | var expect = require('chai').expect, 4 | config = require('./config'), 5 | sprequest = require('sp-request'), 6 | gulp = require('gulp'), 7 | assert = require('stream-assert'), 8 | spsave = require('./../../index'), 9 | url = require('url'), 10 | fs = require('fs'); 11 | 12 | var tests = [ 13 | { 14 | name: 'on-premise user credentials', 15 | creds: config.onpremCreds, 16 | url: config.onpremNtlmEnabledUrl 17 | }, 18 | { 19 | name: 'on-premise addin only', 20 | creds: config.onpremAddinOnly, 21 | url: config.onpremAdfsEnabledUrl 22 | }, 23 | { 24 | name: 'online user credentials', 25 | creds: config.onlineCreds, 26 | url: config.onlineUrl 27 | }, 28 | { 29 | name: 'online addin only', 30 | creds: config.onlineAddinOnly, 31 | url: config.onlineUrl 32 | }, 33 | { 34 | name: 'adfs user credentials', 35 | creds: config.adfsCredentials, 36 | url: config.onpremAdfsEnabledUrl 37 | } 38 | ]; 39 | 40 | tests.forEach(function (test) { 41 | describe(`spsave: integration tests - ${test.name}`, function () { 42 | var spr = sprequest.create(test.creds); 43 | 44 | beforeEach('delete folders', function (done) { 45 | this.timeout(30 * 1000); 46 | 47 | spr.requestDigest(test.url) 48 | .then(digest => { 49 | return Promise.all([spr.post(`${test.url}/_api/web/GetFolderByServerRelativeUrl(@FolderName)` + 50 | `?@FolderName='${encodeURIComponent('SiteAssets/files')}'`, { 51 | headers: { 52 | 'X-RequestDigest': digest, 53 | 'X-HTTP-Method': 'DELETE' 54 | } 55 | })]); 56 | }) 57 | .then(data => { 58 | done(); 59 | }) 60 | .catch(done); 61 | }); 62 | 63 | after('cleaning', function (done) { 64 | this.timeout(30 * 1000); 65 | 66 | spr.requestDigest(test.url) 67 | .then(digest => { 68 | return Promise.all([spr.post(`${test.url}/_api/web/GetFolderByServerRelativeUrl(@FolderName)` + 69 | `?@FolderName='${encodeURIComponent('SiteAssets/files')}'`, { 70 | headers: { 71 | 'X-RequestDigest': digest, 72 | 'X-HTTP-Method': 'DELETE' 73 | } 74 | })]); 75 | }) 76 | .then(data => { 77 | done(); 78 | }) 79 | .catch(done); 80 | }); 81 | 82 | var path = (url.parse(test.url).path).replace(/(\/$)|(\\$)/, ''); 83 | 84 | it('should upload file into the folder', function (done) { 85 | this.timeout(30 * 1000); 86 | var fileName = 'index.js'; 87 | var fileContent = fs.readFileSync(fileName); 88 | var folder = 'SiteAssets/files'; 89 | 90 | gulp.src(fileName) 91 | .pipe(spsave({ 92 | siteUrl: test.url, 93 | folder: folder 94 | }, test.creds)) 95 | .on('finish', function () { 96 | var fileRelativeUrl = `${path}/${folder}/${fileName}`; 97 | 98 | spr.get(`${test.url}/_api/web/GetFileByServerRelativeUrl(@FileUrl)/$value` + 99 | `?@FileUrl='${encodeURIComponent(fileRelativeUrl)}'`, { 100 | responseType: 'buffer' 101 | }).then(data => { 102 | expect(fileContent.equals(data.body)).is.true; 103 | done(); 104 | }) 105 | .catch(done); 106 | }); 107 | }); 108 | 109 | it('should upload file into the folder with automatic base option', function (done) { 110 | this.timeout(30 * 1000); 111 | 112 | var fileContent = fs.readFileSync('test/integration/files/spsave.txt'); 113 | var folder = 'SiteAssets/files'; 114 | 115 | gulp.src('test/integration/files/*.*', { base: 'test' }) 116 | .pipe(spsave({ 117 | siteUrl: test.url, 118 | folder: folder, 119 | flatten: false 120 | }, test.creds)) 121 | .on('finish', function () { 122 | var fileRelativeUrl = `${path}/${folder}/integration/files/spsave.txt`; 123 | 124 | spr.get(`${test.url}/_api/web/GetFileByServerRelativeUrl(@FileUrl)/$value` + 125 | `?@FileUrl='${encodeURIComponent(fileRelativeUrl)}'`, { 126 | responseType: 'buffer' 127 | }).then(data => { 128 | expect(fileContent.equals(data.body)).is.true; 129 | done(); 130 | }) 131 | .catch(done); 132 | }); 133 | }); 134 | 135 | it('should upload file into the folder with base option and flatten', function (done) { 136 | this.timeout(30 * 1000); 137 | 138 | var fileContent = fs.readFileSync('test/integration/files/spsave.txt'); 139 | var folder = 'SiteAssets/files'; 140 | 141 | gulp.src('test/integration/files/*.*', { base: 'test' }) 142 | .pipe(spsave({ 143 | siteUrl: test.url, 144 | folder: folder, 145 | flatten: true 146 | }, test.creds)) 147 | .on('finish', function () { 148 | var fileRelativeUrl = `${path}/${folder}/spsave.txt`; 149 | 150 | spr.get(`${test.url}/_api/web/GetFileByServerRelativeUrl(@FileUrl)/$value` + 151 | `?@FileUrl='${encodeURIComponent(fileRelativeUrl)}'`, { 152 | responseType: 'buffer' 153 | }).then(data => { 154 | expect(fileContent.equals(data.body)).is.true; 155 | done(); 156 | }) 157 | .catch(done); 158 | }); 159 | }); 160 | 161 | it('should not throw an error when processing file further in pipes', function (done) { 162 | this.timeout(30 * 1000); 163 | 164 | var fileContent = fs.readFileSync('test/integration/files/spsave.txt'); 165 | var folder = 'SiteAssets/files'; 166 | 167 | gulp.src('test/integration/files/*.*', { base: 'test' }) 168 | .pipe(spsave({ 169 | siteUrl: test.url, 170 | folder: folder, 171 | flatten: true 172 | }, test.creds)) 173 | .pipe(gulp.dest('./test')) 174 | .on('finish', function () { 175 | done(); 176 | }); 177 | }); 178 | 179 | it('should update metadata', function (done) { 180 | this.timeout(30 * 1000); 181 | var fileName = 'index.js'; 182 | var fileContent = fs.readFileSync(fileName); 183 | var folder = 'SiteAssets/files'; 184 | var title = 'updated by spsave'; 185 | 186 | gulp.src(fileName) 187 | .pipe(spsave({ 188 | siteUrl: test.url, 189 | folder: folder, 190 | filesMetaData: [{ 191 | fileName: fileName, 192 | metadata: { 193 | '__metadata': { type: 'SP.Data.SiteAssetsItem' }, 194 | Title: title 195 | } 196 | }] 197 | }, test.creds)) 198 | .on('finish', function () { 199 | var fileRelativeUrl = `${path}/${folder}/${fileName}`; 200 | 201 | spr.get(`${test.url}/_api/web/GetFileByServerRelativeUrl(@FileUrl)` + 202 | `?@FileUrl='${encodeURIComponent(fileRelativeUrl)}'`).then(data => { 203 | expect(data.body.d.Title).to.equal(title); 204 | done(); 205 | }) 206 | .catch(done); 207 | }); 208 | }); 209 | }); 210 | }); 211 | 212 | --------------------------------------------------------------------------------