forked from Mirrors/doipjs
feat: Add marker fetching logic
This commit is contained in:
parent
46cffbf056
commit
870a544550
5 changed files with 131 additions and 37 deletions
38
src/claim.js
38
src/claim.js
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
const validator = require('validator')
|
const validator = require('validator')
|
||||||
const validUrl = require('valid-url')
|
const validUrl = require('valid-url')
|
||||||
const mergeOptions = require('merge-options')
|
const mergeOptions = require('merge-options')
|
||||||
const proofs = require('./proofs')
|
const request = require('./request')
|
||||||
const verifications = require('./verifications')
|
const verifications = require('./verifications')
|
||||||
const claimDefinitions = require('./claimDefinitions')
|
const claimDefinitions = require('./claimDefinitions')
|
||||||
const defaults = require('./defaults')
|
const defaults = require('./defaults')
|
||||||
|
@ -229,10 +229,44 @@ class Claim {
|
||||||
|
|
||||||
let verificationResult = null
|
let verificationResult = null
|
||||||
let proofData = null
|
let proofData = null
|
||||||
|
let markersData = null
|
||||||
let proofFetchError
|
let proofFetchError
|
||||||
|
|
||||||
|
// Handle markers
|
||||||
try {
|
try {
|
||||||
proofData = await proofs.fetch(claimData, opts)
|
markersData = await request.fetchMarkers(claimData, opts)
|
||||||
|
} catch (err) {
|
||||||
|
proofFetchError = err
|
||||||
|
}
|
||||||
|
if (markersData) {
|
||||||
|
let shouldSkipMatch = false
|
||||||
|
markersData.forEach(marker => {
|
||||||
|
// Skip marker if another was already proven false
|
||||||
|
if (shouldSkipMatch) return
|
||||||
|
|
||||||
|
// Ignore markers that were rejected
|
||||||
|
if (marker.status !== 'fulfilled') return
|
||||||
|
|
||||||
|
let endpointExists
|
||||||
|
switch (marker.value.data.test.type) {
|
||||||
|
case E.MarkerTestType.HTTP_ENDPOINT_MUST_EXIST:
|
||||||
|
endpointExists = marker.value.result && !marker.value.error
|
||||||
|
if ((endpointExists && marker.value.data.test.inverse) || (!(endpointExists || marker.value.data.test.inverse))) {
|
||||||
|
shouldSkipMatch = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (shouldSkipMatch) continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle proof
|
||||||
|
try {
|
||||||
|
proofData = await request.fetchProof(claimData, opts)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
proofFetchError = err
|
proofFetchError = err
|
||||||
}
|
}
|
||||||
|
|
15
src/enums.js
15
src/enums.js
|
@ -146,6 +146,20 @@ const ClaimStatus = {
|
||||||
}
|
}
|
||||||
Object.freeze(ClaimStatus)
|
Object.freeze(ClaimStatus)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How to test a marker
|
||||||
|
* @readonly
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
const MarkerTestType = {
|
||||||
|
/** HTTP endpoint must exist */
|
||||||
|
HTTP_ENDPOINT_MUST_EXIST: 'httpEndpointMustExist'
|
||||||
|
// TODO Implement JSON_CONTAINS
|
||||||
|
// /** JSON data must contain a certain string */
|
||||||
|
// JSON_CONTAINS: 'jsonContains'
|
||||||
|
}
|
||||||
|
Object.freeze(MarkerTestType)
|
||||||
|
|
||||||
exports.ProxyPolicy = ProxyPolicy
|
exports.ProxyPolicy = ProxyPolicy
|
||||||
exports.Fetcher = Fetcher
|
exports.Fetcher = Fetcher
|
||||||
exports.EntityEncodingFormat = EntityEncodingFormat
|
exports.EntityEncodingFormat = EntityEncodingFormat
|
||||||
|
@ -154,3 +168,4 @@ exports.ProofFormat = ProofFormat
|
||||||
exports.ClaimFormat = ClaimFormat
|
exports.ClaimFormat = ClaimFormat
|
||||||
exports.ClaimRelation = ClaimRelation
|
exports.ClaimRelation = ClaimRelation
|
||||||
exports.ClaimStatus = ClaimStatus
|
exports.ClaimStatus = ClaimStatus
|
||||||
|
exports.MarkerTestType = MarkerTestType
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
const Claim = require('./claim')
|
const Claim = require('./claim')
|
||||||
const claimDefinitions = require('./claimDefinitions')
|
const claimDefinitions = require('./claimDefinitions')
|
||||||
const proofs = require('./proofs')
|
const request = require('./request')
|
||||||
const keys = require('./keys')
|
const keys = require('./keys')
|
||||||
const signatures = require('./signatures')
|
const signatures = require('./signatures')
|
||||||
const enums = require('./enums')
|
const enums = require('./enums')
|
||||||
|
@ -26,7 +26,7 @@ const fetcher = require('./fetcher')
|
||||||
|
|
||||||
exports.Claim = Claim
|
exports.Claim = Claim
|
||||||
exports.claimDefinitions = claimDefinitions
|
exports.claimDefinitions = claimDefinitions
|
||||||
exports.proofs = proofs
|
exports.request = request
|
||||||
exports.keys = keys
|
exports.keys = keys
|
||||||
exports.signatures = signatures
|
exports.signatures = signatures
|
||||||
exports.enums = enums
|
exports.enums = enums
|
||||||
|
|
|
@ -19,7 +19,7 @@ const utils = require('./utils')
|
||||||
const E = require('./enums')
|
const E = require('./enums')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module proofs
|
* @module request
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,7 +33,7 @@ const E = require('./enums')
|
||||||
* @param {object} opts - Options to enable the request
|
* @param {object} opts - Options to enable the request
|
||||||
* @returns {Promise<object|string>}
|
* @returns {Promise<object|string>}
|
||||||
*/
|
*/
|
||||||
const fetch = (data, opts) => {
|
const fetchProof = (data, opts) => {
|
||||||
switch (data.proof.request.fetcher) {
|
switch (data.proof.request.fetcher) {
|
||||||
case E.Fetcher.HTTP:
|
case E.Fetcher.HTTP:
|
||||||
data.proof.request.data.format = data.proof.request.format
|
data.proof.request.data.format = data.proof.request.format
|
||||||
|
@ -44,22 +44,49 @@ const fetch = (data, opts) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jsEnv.isNode) {
|
if (jsEnv.isNode) {
|
||||||
return handleNodeRequests(data, opts)
|
return handleNodeRequests(data.proof, opts, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleBrowserRequests(data, opts)
|
return handleBrowserRequests(data.proof, opts, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBrowserRequests = (data, opts) => {
|
/**
|
||||||
|
* Delegate the marker requests to the correct fetcher.
|
||||||
|
* This method uses the current environment (browser/node), certain values from
|
||||||
|
* the `data` parameter and the proxy policy set in the `opts` parameter to
|
||||||
|
* choose the right approach to fetch the proof. An error will be thrown if no
|
||||||
|
* approach is possible.
|
||||||
|
* @async
|
||||||
|
* @param {object} data - Data from a claim definition
|
||||||
|
* @param {object} opts - Options to enable the request
|
||||||
|
* @returns {Promise<Array<object>>}
|
||||||
|
*/
|
||||||
|
const fetchMarkers = async (data, opts) => {
|
||||||
|
const promises = []
|
||||||
|
|
||||||
|
if (!(data.markers && data.markers.length > 0)) throw new Error('No markers found')
|
||||||
|
|
||||||
|
data.markers.forEach(marker => {
|
||||||
|
if (jsEnv.isNode) {
|
||||||
|
promises.push(handleNodeRequests(marker, opts, true))
|
||||||
|
} else {
|
||||||
|
promises.push(handleBrowserRequests(marker, opts, true))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return Promise.allSettled(promises)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBrowserRequests = (data, opts, alwaysResolve) => {
|
||||||
switch (opts.proxy.policy) {
|
switch (opts.proxy.policy) {
|
||||||
case E.ProxyPolicy.ALWAYS:
|
case E.ProxyPolicy.ALWAYS:
|
||||||
return createProxyRequestPromise(data, opts)
|
return createProxyRequestPromise(data, opts, alwaysResolve)
|
||||||
|
|
||||||
case E.ProxyPolicy.NEVER:
|
case E.ProxyPolicy.NEVER:
|
||||||
switch (data.proof.request.access) {
|
switch (data.request.access) {
|
||||||
case E.ProofAccess.GENERIC:
|
case E.ProofAccess.GENERIC:
|
||||||
case E.ProofAccess.GRANTED:
|
case E.ProofAccess.GRANTED:
|
||||||
return createDefaultRequestPromise(data, opts)
|
return createDefaultRequestPromise(data, opts, alwaysResolve)
|
||||||
case E.ProofAccess.NOCORS:
|
case E.ProofAccess.NOCORS:
|
||||||
case E.ProofAccess.SERVER:
|
case E.ProofAccess.SERVER:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -70,15 +97,13 @@ const handleBrowserRequests = (data, opts) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
case E.ProxyPolicy.ADAPTIVE:
|
case E.ProxyPolicy.ADAPTIVE:
|
||||||
switch (data.proof.request.access) {
|
switch (data.request.access) {
|
||||||
case E.ProofAccess.GENERIC:
|
case E.ProofAccess.GENERIC:
|
||||||
return createFallbackRequestPromise(data, opts)
|
|
||||||
case E.ProofAccess.NOCORS:
|
|
||||||
return createProxyRequestPromise(data, opts)
|
|
||||||
case E.ProofAccess.GRANTED:
|
case E.ProofAccess.GRANTED:
|
||||||
return createFallbackRequestPromise(data, opts)
|
return createFallbackRequestPromise(data, opts, alwaysResolve)
|
||||||
|
case E.ProofAccess.NOCORS:
|
||||||
case E.ProofAccess.SERVER:
|
case E.ProofAccess.SERVER:
|
||||||
return createProxyRequestPromise(data, opts)
|
return createProxyRequestPromise(data, opts, alwaysResolve)
|
||||||
default:
|
default:
|
||||||
throw new Error('Invalid proof access value')
|
throw new Error('Invalid proof access value')
|
||||||
}
|
}
|
||||||
|
@ -88,47 +113,56 @@ const handleBrowserRequests = (data, opts) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleNodeRequests = (data, opts) => {
|
const handleNodeRequests = (data, opts, alwaysResolve) => {
|
||||||
switch (opts.proxy.policy) {
|
switch (opts.proxy.policy) {
|
||||||
case E.ProxyPolicy.ALWAYS:
|
case E.ProxyPolicy.ALWAYS:
|
||||||
return createProxyRequestPromise(data, opts)
|
return createProxyRequestPromise(data, opts, alwaysResolve)
|
||||||
|
|
||||||
case E.ProxyPolicy.NEVER:
|
case E.ProxyPolicy.NEVER:
|
||||||
return createDefaultRequestPromise(data, opts)
|
return createDefaultRequestPromise(data, opts, alwaysResolve)
|
||||||
|
|
||||||
case E.ProxyPolicy.ADAPTIVE:
|
case E.ProxyPolicy.ADAPTIVE:
|
||||||
return createFallbackRequestPromise(data, opts)
|
return createFallbackRequestPromise(data, opts, alwaysResolve)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error('Invalid proxy policy')
|
throw new Error('Invalid proxy policy')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const createDefaultRequestPromise = (data, opts) => {
|
const createDefaultRequestPromise = (data, opts, alwaysResolve) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fetcher[data.proof.request.fetcher]
|
fetcher[data.request.fetcher]
|
||||||
.fn(data.proof.request.data, opts)
|
.fn(data.request.data, opts)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return resolve({
|
return resolve({
|
||||||
fetcher: data.proof.request.fetcher,
|
fetcher: data.request.fetcher,
|
||||||
data: data,
|
data: data,
|
||||||
viaProxy: false,
|
viaProxy: false,
|
||||||
result: res
|
result: res
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
if (alwaysResolve) {
|
||||||
|
return resolve({
|
||||||
|
fetcher: 'http',
|
||||||
|
data: data,
|
||||||
|
viaProxy: true,
|
||||||
|
error: err
|
||||||
|
})
|
||||||
|
} else {
|
||||||
return reject(err)
|
return reject(err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const createProxyRequestPromise = (data, opts) => {
|
const createProxyRequestPromise = (data, opts, alwaysResolve) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let proxyUrl
|
let proxyUrl
|
||||||
try {
|
try {
|
||||||
proxyUrl = utils.generateProxyURL(
|
proxyUrl = utils.generateProxyURL(
|
||||||
data.proof.request.fetcher,
|
data.request.fetcher,
|
||||||
data.proof.request.data,
|
data.request.data,
|
||||||
opts
|
opts
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -137,8 +171,8 @@ const createProxyRequestPromise = (data, opts) => {
|
||||||
|
|
||||||
const requestData = {
|
const requestData = {
|
||||||
url: proxyUrl,
|
url: proxyUrl,
|
||||||
format: data.proof.request.format,
|
format: data.request.format,
|
||||||
fetcherTimeout: fetcher[data.proof.request.fetcher].timeout
|
fetcherTimeout: fetcher[data.request.fetcher].timeout
|
||||||
}
|
}
|
||||||
fetcher.http
|
fetcher.http
|
||||||
.fn(requestData, opts)
|
.fn(requestData, opts)
|
||||||
|
@ -151,19 +185,28 @@ const createProxyRequestPromise = (data, opts) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
if (alwaysResolve) {
|
||||||
|
return resolve({
|
||||||
|
fetcher: 'http',
|
||||||
|
data: data,
|
||||||
|
viaProxy: true,
|
||||||
|
error: err
|
||||||
|
})
|
||||||
|
} else {
|
||||||
return reject(err)
|
return reject(err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const createFallbackRequestPromise = (data, opts) => {
|
const createFallbackRequestPromise = (data, opts, alwaysResolve) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
createDefaultRequestPromise(data, opts)
|
createDefaultRequestPromise(data, opts, alwaysResolve)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return resolve(res)
|
return resolve(res)
|
||||||
})
|
})
|
||||||
.catch((err1) => {
|
.catch((err1) => {
|
||||||
createProxyRequestPromise(data, opts)
|
createProxyRequestPromise(data, opts, alwaysResolve)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return resolve(res)
|
return resolve(res)
|
||||||
})
|
})
|
||||||
|
@ -174,4 +217,5 @@ const createFallbackRequestPromise = (data, opts) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = fetch
|
exports.fetchProof = fetchProof
|
||||||
|
exports.fetchMarkers = fetchMarkers
|
|
@ -38,6 +38,7 @@ const pattern = {
|
||||||
return _.isString(x) || _.isNull(x)
|
return _.isString(x) || _.isNull(x)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
markers: _.isArray,
|
||||||
proof: {
|
proof: {
|
||||||
uri: (x) => {
|
uri: (x) => {
|
||||||
return _.isString(x) || _.isNull(x)
|
return _.isString(x) || _.isNull(x)
|
||||||
|
|
Loading…
Reference in a new issue