Merge pull request #552 from lbryio/498-incorrect-short-url

498 incorrect short url
This commit is contained in:
Bill Bittner 2018-08-03 08:05:46 -07:00 committed by GitHub
commit c32daba56c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 153 additions and 191 deletions

View file

@ -3,7 +3,7 @@ import * as actions from '../constants/show_action_types';
import { onRequestError, onNewChannelRequest, onNewAssetRequest } from '../actions/show'; import { onRequestError, onNewChannelRequest, onNewAssetRequest } from '../actions/show';
import { newAssetRequest } from '../sagas/show_asset'; import { newAssetRequest } from '../sagas/show_asset';
import { newChannelRequest } from '../sagas/show_channel'; import { newChannelRequest } from '../sagas/show_channel';
import lbryUri from '../utils/lbryUri'; import lbryUri from '../../../utils/lbryUri';
function * parseAndUpdateIdentifierAndClaim (modifier, claim) { function * parseAndUpdateIdentifierAndClaim (modifier, claim) {
// this is a request for an asset // this is a request for an asset

View file

@ -1,85 +0,0 @@
module.exports = {
REGEXP_INVALID_CLAIM : /[^A-Za-z0-9-]/g,
REGEXP_INVALID_CHANNEL: /[^A-Za-z0-9-@]/g,
REGEXP_ADDRESS : /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/,
CHANNEL_CHAR : '@',
parseIdentifier : function (identifier) {
const componentsRegex = new RegExp(
'([^:$#/]*)' + // value (stops at the first separator or end)
'([:$#]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end)
);
const [proto, value, modifierSeperator, modifier] = componentsRegex // eslint-disable-line no-unused-vars
.exec(identifier)
.map(match => match || null);
// Validate and process name
if (!value) {
throw new Error(`Check your URL. No channel name provided before "${modifierSeperator}"`);
}
const isChannel = value.startsWith(module.exports.CHANNEL_CHAR);
const channelName = isChannel ? value : null;
let claimId;
if (isChannel) {
if (!channelName) {
throw new Error('Check your URL. No channel name after "@".');
}
const nameBadChars = (channelName).match(module.exports.REGEXP_INVALID_CHANNEL);
if (nameBadChars) {
throw new Error(`Check your URL. Invalid characters in channel name: "${nameBadChars.join(', ')}".`);
}
} else {
claimId = value;
}
// Validate and process modifier
let channelClaimId;
if (modifierSeperator) {
if (!modifier) {
throw new Error(`Check your URL. No modifier provided after separator "${modifierSeperator}"`);
}
if (modifierSeperator === ':') {
channelClaimId = modifier;
} else {
throw new Error(`Check your URL. The "${modifierSeperator}" modifier is not currently supported`);
}
}
return {
isChannel,
channelName,
channelClaimId: channelClaimId || null,
claimId : claimId || null,
};
},
parseClaim: function (name) {
const componentsRegex = new RegExp(
'([^:$#/.]*)' + // name (stops at the first extension)
'([:$#.]?)([^/]*)' // extension separator, extension (stops at the first path separator or end)
);
const [proto, claimName, extensionSeperator, extension] = componentsRegex // eslint-disable-line no-unused-vars
.exec(name)
.map(match => match || null);
// Validate and process name
if (!claimName) {
throw new Error('Check your URL. No claim name provided before "."');
}
const nameBadChars = (claimName).match(module.exports.REGEXP_INVALID_CLAIM);
if (nameBadChars) {
throw new Error(`Check your URL. Invalid characters in claim name: "${nameBadChars.join(', ')}".`);
}
// Validate and process extension
if (extensionSeperator) {
if (!extension) {
throw new Error(`Check your URL. No file extension provided after separator "${extensionSeperator}".`);
}
if (extensionSeperator !== '.') {
throw new Error(`Check your URL. The "${extensionSeperator}" separator is not supported in the claim name.`);
}
}
return {
claimName,
extension: extension || null,
};
},
};

View file

@ -12,12 +12,12 @@
"fix": "eslint . --fix", "fix": "eslint . --fix",
"lint": "eslint .", "lint": "eslint .",
"precommit": "eslint .", "precommit": "eslint .",
"start": "npm run server", "prestart": "builder run bundle",
"start:dev": "builder concurrent transpile:dev bundle:dev server:dev", "start": "node server.js",
"server": "node server.js", "start:build": "builder run start",
"server:dev": "nodemon server.js",
"test": "mocha --recursive", "test": "mocha --recursive",
"test:no-lbc": "npm test -- --grep @usesLbc --invert", "test:no-lbc": "npm test -- --grep @usesLbc --invert",
"test:server": "mocha --recursive './server/**/*.test.js'",
"transpile": "builder concurrent transpile:server transpile:client transpile:client_custom", "transpile": "builder concurrent transpile:server transpile:client transpile:client_custom",
"transpile:dev": "builder concurrent transpile:server:dev transpile:client:dev transpile:client_custom:dev", "transpile:dev": "builder concurrent transpile:server:dev transpile:client:dev transpile:client_custom:dev",
"transpile:server": "babel server/render/src -d server/render/build", "transpile:server": "babel server/render/src -d server/render/build",

View file

@ -0,0 +1,15 @@
const chai = require('chai');
const expect = chai.expect;
describe('#parsePublishApiRequestBody()', function () {
const parsePublishApiRequestBody = require('./parsePublishApiRequestBody.js');
it('should throw an error if no body', function () {
expect(parsePublishApiRequestBody.bind(this, null)).to.throw();
});
it('should throw an error if no body.name', function () {
const bodyNoName = {};
expect(parsePublishApiRequestBody.bind(this, bodyNoName)).to.throw();
});
});

View file

@ -0,0 +1,51 @@
const chai = require('chai');
const expect = chai.expect;
describe('#parsePublishApiRequestFiles()', function () {
const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js');
it('should throw an error if no files', function () {
expect(parsePublishApiRequestFiles.bind(this, null)).to.throw();
});
it('should throw an error if no files.file', function () {
const filesNoFile = {};
expect(parsePublishApiRequestFiles.bind(this, filesNoFile)).to.throw();
});
it('should throw an error if file.size is too large', function () {
const filesTooBig = {
file: {
name: 'file.jpg',
path: '/path/to/file.jpg',
type: 'image/jpg',
size: 10000001,
},
};
expect(parsePublishApiRequestFiles.bind(this, filesTooBig)).to.throw();
});
it('should throw error if not an accepted file type', function () {
const filesWrongType = {
file: {
name: 'file.jpg',
path: '/path/to/file.jpg',
type: 'someType/ext',
size: 10000000,
},
};
expect(parsePublishApiRequestFiles.bind(this, filesWrongType)).to.throw();
});
it('should throw NO error if no problems', function () {
const filesNoProblems = {
file: {
name: 'file.jpg',
path: '/path/to/file.jpg',
type: 'image/jpg',
size: 10000000,
},
};
expect(parsePublishApiRequestFiles.bind(this, filesNoProblems)).to.not.throw();
});
});

View file

@ -1,5 +1,5 @@
const logger = require('winston'); const logger = require('winston');
const lbryUri = require('../../utils/lbryUri'); const lbryUri = require('../../../../utils/lbryUri');
const getOEmbedDataForChannel = require('./getOEmbedDataForChannel'); const getOEmbedDataForChannel = require('./getOEmbedDataForChannel');
const getOEmbedDataForAsset = require('./getOEmbedDataForAsset'); const getOEmbedDataForAsset = require('./getOEmbedDataForAsset');

View file

@ -3,7 +3,7 @@ const logger = require('winston');
const { sendGAServeEvent } = require('../../../utils/googleAnalytics'); const { sendGAServeEvent } = require('../../../utils/googleAnalytics');
const handleShowRender = require('../../../render/build/handleShowRender.js'); const handleShowRender = require('../../../render/build/handleShowRender.js');
const lbryUri = require('../../utils/lbryUri.js'); const lbryUri = require('../../../../utils/lbryUri.js');
const determineRequestType = require('../utils/determineRequestType.js'); const determineRequestType = require('../utils/determineRequestType.js');
const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js'); const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js');

View file

@ -3,7 +3,7 @@ const logger = require('winston');
const { sendGAServeEvent } = require('../../../utils/googleAnalytics'); const { sendGAServeEvent } = require('../../../utils/googleAnalytics');
const handleShowRender = require('../../../render/build/handleShowRender.js'); const handleShowRender = require('../../../render/build/handleShowRender.js');
const lbryUri = require('../../utils/lbryUri.js'); const lbryUri = require('../../../../utils/lbryUri.js');
const determineRequestType = require('../utils/determineRequestType.js'); const determineRequestType = require('../utils/determineRequestType.js');
const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js'); const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js');

View file

@ -401,5 +401,10 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
}); });
}; };
Claim.getCurrentHeight = function () {
return this
.max('height');
};
return Claim; return Claim;
}; };

View file

@ -1,3 +1,5 @@
const db = require('../index.js');
const createClaimRecordDataAfterPublish = (certificateId, channelName, fileName, fileType, publishParams, publishResults) => { const createClaimRecordDataAfterPublish = (certificateId, channelName, fileName, fileType, publishParams, publishResults) => {
const { const {
name, name,
@ -17,21 +19,24 @@ const createClaimRecordDataAfterPublish = (certificateId, channelName, fileName,
nout, nout,
} = publishResults; } = publishResults;
return { return db.Claim.getCurrentHeight()
name, .then(height => {
claimId, return {
title, name,
description, claimId,
address, title,
thumbnail, description,
outpoint : `${txid}:${nout}`, address,
height : 0, thumbnail,
contentType: fileType, outpoint : `${txid}:${nout}`,
nsfw, height,
amount, contentType: fileType,
certificateId, nsfw,
channelName, amount,
}; certificateId,
channelName,
};
});
}; };
module.exports = { module.exports = {

View file

@ -0,0 +1,42 @@
const chai = require('chai');
const expect = chai.expect;
describe('#parsePublishApiRequestBody()', function () {
const returnShortId = require('./returnShortId.js');
let dummyClaimsArray;
let dummyLongId;
it('should thow an error if the claimId is not in the claim list', function () {
dummyClaimsArray = [
{claimId: 'a123456789'},
{claimId: 'b123456789'},
{claimId: 'c123456789'},
];
dummyLongId = 'xxxxxxxxxx';
expect(returnShortId.bind(this, dummyClaimsArray, dummyLongId)).to.throw();
});
it('should return the shortest unique claim id', function () {
dummyClaimsArray = [
{claimId: 'a123456789'},
{claimId: 'b123456789'},
{claimId: 'c123456789'},
];
dummyLongId = 'c123456789';
expect(returnShortId(dummyClaimsArray, dummyLongId)).to.equal('c');
});
it('if there is a conflict between unqiue ids, it should give preference to the one with the lowest height', function () {
dummyClaimsArray = [
{claimId: 'a123456789', height: 10},
{claimId: 'ab12345678', height: 11},
{claimId: 'ab12341111', height: 12},
];
dummyLongId = 'a123456789';
expect(returnShortId(dummyClaimsArray, dummyLongId)).to.equal('a');
dummyLongId = 'ab12345678';
expect(returnShortId(dummyClaimsArray, dummyLongId)).to.equal('ab');
dummyLongId = 'ab12341111';
expect(returnShortId(dummyClaimsArray, dummyLongId)).to.equal('ab12341');
});
});

View file

@ -1,3 +1,5 @@
require('../module-alias-boilerplate.js');
const chai = require('chai'); const chai = require('chai');
const expect = chai.expect; const expect = chai.expect;
const chaiHttp = require('chai-http'); const chaiHttp = require('chai-http');

View file

@ -0,0 +1,4 @@
// set up aliases
const moduleAlias = require('module-alias');
const customAliases = require('../utils/createModuleAliases.js')();
moduleAlias.addAliases(customAliases);

View file

@ -1,72 +0,0 @@
const chai = require('chai');
const expect = chai.expect;
describe('publish utils', function () {
describe('#parsePublishApiRequestBody()', function () {
const parsePublishApiRequestBody = require('../../../server/controllers/api/claim/publish/parsePublishApiRequestBody.js');
it('should throw an error if no body', function () {
expect(parsePublishApiRequestBody.bind(this, null)).to.throw();
});
it('should throw an error if no body.name', function () {
const bodyNoName = {};
expect(parsePublishApiRequestBody.bind(this, bodyNoName)).to.throw();
});
});
describe('#parsePublishApiRequestFiles()', function () {
const parsePublishApiRequestFiles = require('../../../server/controllers/api/claim/publish/parsePublishApiRequestFiles.js');
it('should throw an error if no files', function () {
expect(parsePublishApiRequestFiles.bind(this, null)).to.throw();
});
it('should throw an error if no files.file', function () {
const filesNoFile = {};
expect(parsePublishApiRequestFiles.bind(this, filesNoFile)).to.throw();
});
it('should throw an error if file.size is too large', function () {
const filesTooBig = {
file: {
name: 'file.jpg',
path: '/path/to/file.jpg',
type: 'image/jpg',
size: 10000001,
},
};
expect(parsePublishApiRequestFiles.bind(this, filesTooBig)).to.throw();
});
it('should throw error if not an accepted file type', function () {
const filesWrongType = {
file: {
name: 'file.jpg',
path: '/path/to/file.jpg',
type: 'someType/ext',
size: 10000000,
},
};
expect(parsePublishApiRequestFiles.bind(this, filesWrongType)).to.throw();
});
it('should throw NO error if no problems', function () {
const filesNoProblems = {
file: {
name: 'file.jpg',
path: '/path/to/file.jpg',
type: 'image/jpg',
size: 10000000,
},
};
expect(parsePublishApiRequestFiles.bind(this, filesNoProblems)).to.not.throw();
});
});
describe('#parsePublishApiChannel()', function () {
it('should pass the tests I write here');
});
});

View file

@ -26,6 +26,7 @@ module.exports = () => {
let moduleAliases = {}; let moduleAliases = {};
// aliases for configs // aliases for configs
moduleAliases['@config'] = resolve(`config`); moduleAliases['@config'] = resolve(`config`);
moduleAliases['@devConfig'] = resolve(`devConfig`);
// create specific aliases for locally defined components // create specific aliases for locally defined components
moduleAliases = addAlliasesForFolder('containers', moduleAliases); moduleAliases = addAlliasesForFolder('containers', moduleAliases);

View file

@ -1,20 +1,16 @@
const logger = require('winston');
module.exports = { module.exports = {
REGEXP_INVALID_CLAIM : /[^A-Za-z0-9-]/g, REGEXP_INVALID_CLAIM : /[^A-Za-z0-9-]/g,
REGEXP_INVALID_CHANNEL: /[^A-Za-z0-9-@]/g, REGEXP_INVALID_CHANNEL: /[^A-Za-z0-9-@]/g,
REGEXP_ADDRESS : /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/, REGEXP_ADDRESS : /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/,
CHANNEL_CHAR : '@', CHANNEL_CHAR : '@',
parseIdentifier : function (identifier) { parseIdentifier : function (identifier) {
logger.debug('parsing identifier:', identifier);
const componentsRegex = new RegExp( const componentsRegex = new RegExp(
'([^:$#/]*)' + // value (stops at the first separator or end) '([^:$#/]*)' + // value (stops at the first separator or end)
'([:$#]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end) '([:$#]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end)
); );
const [proto, value, modifierSeperator, modifier] = componentsRegex const [, value, modifierSeperator, modifier] = componentsRegex
.exec(identifier) .exec(identifier)
.map(match => match || null); .map(match => match || null);
logger.debug(`${proto}, ${value}, ${modifierSeperator}, ${modifier}`);
// Validate and process name // Validate and process name
if (!value) { if (!value) {
@ -56,15 +52,13 @@ module.exports = {
}; };
}, },
parseClaim: function (claim) { parseClaim: function (claim) {
logger.debug('parsing name:', claim);
const componentsRegex = new RegExp( const componentsRegex = new RegExp(
'([^:$#/.]*)' + // name (stops at the first modifier) '([^:$#/.]*)' + // name (stops at the first modifier)
'([:$#.]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end) '([:$#.]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end)
); );
const [proto, claimName, modifierSeperator, modifier] = componentsRegex const [, claimName, modifierSeperator, modifier] = componentsRegex
.exec(claim) .exec(claim)
.map(match => match || null); .map(match => match || null);
logger.debug(`${proto}, ${claimName}, ${modifierSeperator}, ${modifier}`);
// Validate and process name // Validate and process name
if (!claimName) { if (!claimName) {
@ -86,18 +80,18 @@ module.exports = {
// return results // return results
return { return {
claimName, claimName,
extension: modifier || null,
}; };
}, },
parseModifier: function (claim) { parseModifier: function (claim) {
logger.debug('parsing modifier:', claim);
const componentsRegex = new RegExp( const componentsRegex = new RegExp(
'([^:$#/.]*)' + // name (stops at the first modifier) '([^:$#/.]*)' + // name (stops at the first modifier)
'([:$#.]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end) '([:$#.]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end)
); );
const [proto, claimName, modifierSeperator, modifier] = componentsRegex const [ , , modifierSeperator ] = componentsRegex
.exec(claim) .exec(claim)
.map(match => match || null); .map(match => match || null);
logger.debug(`${proto}, ${claimName}, ${modifierSeperator}, ${modifier}`);
// Validate and process modifier // Validate and process modifier
let hasFileExtension = false; let hasFileExtension = false;
if (modifierSeperator) { if (modifierSeperator) {