anrom-jive-app-tools

1.1.0 • Public • Published

anrom-jive-app-tools

A set of simple tools that helps creating Jive tiles and apps. Also includes some common utilities

Requirements

  • npm version 6.4.0 or above
  • node version 8.12.0 or above

Installation

npm install anrom-jive-app-tools

Usage

Different sets of tools in this package can be imported in several ways.

You can import them all in a single variable:
import tools from 'anrom-jive-app-tools'
import {tileProps} from 'anrom-jive-app-tools'

Or import a number of functions from a single toolset if you don't intend to use others and don't want to make your bundle even larger:
import {jiveDate2Moment} from 'anrom-jive-app-tools/dateUtils'

Contents

tileProps

import {
    tileId, 
    tilePath, 
    tileUrl, 
    parent, 
    getContainerAsync
} from 'anrom-jive-app-tools/tileProps'

Gives you access to the tile information before DOM loads

tileId()

returns: String; Id of a tile (string), e.g. 1582.

Can be useful if your domain security is turned off and you want to access tile's frame from within a tile.

Usage example:

//Jive main page code containing a tile
<iframe id="__gadget_j-app-tile-parent-1582"... />    
//Tile code
import {tileId} from 'anrom-jive-app-tools/tileProps'
const iframe = window.parent.document.querySelector('#__gadget_j-app-tile-parent-' + tileId())

Note: returns undefined if called from an app

tilePath()

returns: String; Relative path to a tile's folder inside jive.

Helps you load the resources directly. (/resources/add-ons/9abb17d6-e0a4-4e3e-b3d7-dc29f1a9edae/6dc9c116f0/tiles/tile-search)

This function comes in handy when you need to use a file that resides in tile's package, but you don't know it's final address, because tile will have a new URL after each upload of the package. Sometimes you can just use relative URLs and rely on webpack loaders to place file where they have to be. But in case of fonts, for example, you can't.

Usage example:

import {tilePath} from 'anrom-jive-app-tools/tileProps'
import LatoRegularWOFF from '../fonts/Lato-Regular.woff'
 
export default function FontLoader() {
    return <style>{
        `@font-face {
            font-family: 'Lato-Regular';
            font-style: normal;
            font-weight: 400;
            src: url(${tilePath()}/fonts/${LatoRegularWOFF}) format('woff');
        }`
    }</style>
}

tileUrl()

returns: Object; Full URL of a tile, with query params

Output example:

{
  "protocol": "http:",
  "slashes": true,
  "auth": null,
  "host": "192.168.1.200:8080",
  "port": "8080",
  "hostname": "192.168.1.200",
  "hash": null,
  "search": "?features=os-2.5,core-v3,jq-1.11,tile,responsive&ts=1548412809958&syn_app=e502h&ref_e502h=tileInstance:1063",
  "query": {
    "features": "os-2.5,core-v3,jq-1.11,tile,responsive",
    "ts": "1548412809958",
    "syn_app": "e502h",
    "ref_e502h": "tileInstance:1063"
  },
  "pathname": "/resources/add-ons/a8e957c9-a128-415d-9138-0f5dfffa4e7e/4f40de669b/tiles/product-tile-activity/view.html",
  "path": "/resources/add-ons/a8e957c9-a128-415d-9138-0f5dfffa4e7e/4f40de669b/tiles/product-tile-activity/view.html?features=os-2.5,core-v3,jq-1.11,tile,responsive&ts=1548412809958&syn_app=e502h&ref_e502h=tileInstance:1063",
  "href": "http://192.168.1.200:8080/resources/add-ons/a8e957c9-a128-415d-9138-0f5dfffa4e7e/4f40de669b/tiles/product-tile-activity/view.html?features=os-2.5,core-v3,jq-1.11,tile,responsive&ts=1548412809958&syn_app=e502h&ref_e502h=tileInstance:1063"
}

parent()

returns: String; URL of your root jive instance (without the subdomain added by domain security settings) e.g. http://myjivesite.com instead of http://apps.myjivesite.com

async getContainerAsync()

returns: Promise(Object); Jive place in which tile instance is located

Usage example:

const currentPlace = await getContainerAsync()
const content = await promiseRestGet('/places/' + currentPlace.placeID + '/contents')

utils

Regular utility functions that is often used in jive tile development

import {
    pause, 
    unescapeHtmlEntities, 
    splitArray, 
    abridge, 
    getCacheableImage, 
    findContentImage, 
    getContentImage, 
    getImagelessHTML, 
    jsonCopy, 
    isEmptyObject
} from 'anrom-jive-app-tools/utils'

async pause(milliseconds)

returns: Promise(none);

Promise/async wrapper for setTimeout. If you need to set your code execution on hold, use it:

async function myFunc(){
    /* do stuff */
    await pause(500)
    /* do another stuff */
} 

unescapeHtmlEntities(text)

returns: String;

Text returned by jive API can contain special escape characters like &amp; which stands for &. With usual react rendering (<div>{text}</div>) these characters are not being unescaped, which means it will appear in your tile as "Chip &amp; Dale". To avoid that, use this function:
<div>{unescapeHtmlEntities(text)}</div>

This function is also stripping all HTML tags and returns plain text, so it can be used for receiving plain preview text from content.text

splitArray(array, columns)

returns: Array(Arrays);

Used to split a plain array into several relatively equal chunks (relatively - because last chunk can be shorter if division is with a remainder). This is usually used to split data model array to display in several columns

Usage example:

const columns = splitArray([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17], 4)
// [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17]]

abridge(text, [length=160])

returns: String;

Used to display text, smartly truncated to end with a whole word and with '...' at the end. If there's only one word left after truncation - it will be left truncated and appedned with '...'. Also it will trim the ., ,, : and ; at the end of last word, but leave ? or !

getCacheableImage(initialImageURL, [imageWidth=500, thumbnail=false])

returns: String; Transformed URL if initialImageURL matches the pattern, initialImageURL if not; Any falsy value, if given as initialImageURL

Check image URL against few rules, detecting images hosted within jive. If such image is detected - special URL is being returned, allowing image caching and size reduction. It's highly recommended to use it when rendering images in terms of performance.

Note: I don't know how "thumbnail" mode is different from default, I'm just passing this argument to the URL

findContentImage(contentItem, [mode='regexp', fallback=false])

returns: String; false if no image found; null if contentItem is malformed

Searches jive API content item (document, discussion etc) for first content image URL.

params:

  • contentItem - jive API content item (documen, discussion etc)
  • mode ("regexp") - select mode (see below)
  • fallback (true) - if set to true or omitted and the mode parameter is not api - it will fall back to jive API in case the selected method hasn't found any images.

modes:
By default regular expression ("regexp") mode is used. It's recommended to use it that way, but if you're experiencing some edge cases, you may want to use other modes. Though, each of them has it's own flaw.

api mode relies on jive API's native contentImages and thumbnailURL fields. It's fastest way to detect an image. The drawback is that at least contentImages field is populated on content creation and then never being updated, even if the content item does.

jquery mode creates the entire content's DOM in browser memory and then searches for an image. It's the most precise way, but it's also a slowest one. Also, all images in the parsed content are being requested from the network. This can severely hit the overall performance. This mode is only recommended as a temporal solution if regexp mode fails and is not yet fixed by me :)

getContentImage(contentItem, [options])

returns: String;

The combination of the previous two functions. Finds image in jive content and if it's a jive-hosted image - converts it's URL to a cacheable and resized one. Recommended for usage by default when the job is to display a content preview.

options parameter is optional. Option names and their defaults are:

{
  "imageWidth": 500,
  "mode": "regexp",
  "thumbnail": false,
  "fallback": true
}

(they're the same as default parameters of the functions above)

getImagelessHTML(htmlText)

returns: String;
Sometimes you may need html stripped of all images. This is a function for such cases.

jsonCopy(object)

returns: Object;

Makes a deep copy of JS data object (ensuring that there will be no references to it's members in other parts of a program). Don't use for objects with function values, class instances etc. Also fixes the issue when Array prototype is polluted and thus array instanceof Array check fails.

Usage example:

const immutableConfig = jsonCopy(config)

isEmptyObject(object)

returns: Boolean;
Returns true if the given object has no values ({}), false otherwise.

dateUtils

Working with dates in jive requires same actions all the time. I grouped them in a few functions

import {
    jiveDateFormat, 
    jiveDate2Moment, 
    moment2JiveDate, 
    jiveDate2TS, 
    TS2JiveDate
} from 'anrom-jive-app-tools/dateUtils'

jiveDateFormat

type: String;
Simple string with the value YYYY-MM-DDTHH:mm:ss.SSSZ, which is a formatting pattern for moment.js

Next functions are quite self-explanatory:

jiveDate2Moment(jiveDate)

moment2JiveDate(momentDate)

jiveDate2TS(JiveDate)

TS2JiveDate(timestamp)

fetchPromise

import {
    promiseOsapiRequest,
    promiseHttpGet,
    promiseHttpPost,
    promiseRestGet,
    promiseRestPost,
    promiseRestPut,
    promiseRestDelete,
    promiseRestBatch,
    promiseOsapiBatch,
    CurrentPlace
} from 'anrom-jive-app-tools/fetchPromise'

Provides a set of async promise-wrappers for jive tile osapi network methods.
All "promise..." functions are throwing the entire response as a reject/error:

Use as promise (ES5 usage):

function getData(){
    promiseRestGet('/people/@me').then(function(response){
        /* do your stuff */
    }).catch(function(error){
        /* process error */
    })
}

Use as async function (ES2017+ usage):

async function getData(){
    try {
        const viewer = await promiseRestGet('/people/@me')
        /* do your stuff */
    } catch (error) {
        /* process error */
    }
}

async promiseOsapiRequest(functionOrExecutable)

returns: Promise(Object)

Takes jive osapi executabe or function that takes osapi.jive.corev3 as a single argument and returns such executable:

Usage option 1:

const viewer = await promiseOsapiRequest(osapi.jive.corev3.people.getViewer())

This also means that you can put any executable OSAPI function including those returned from an OSAPI request, like the "getNextPage":

const contentResponse = await promiseOsapiRequest(osapi.jive.corev3.contents.get())
const nextPage = await promiseOsapiRequest(contentResponse.getNextPage())

Usage option 2:

const viewer = await promiseOsapiRequest(api => api.people.getViewer())

async promiseHttpGet(...args)

Promise/async wrapper for osapi.http.get. Parameters are forwarded without change.
Use it to access non-jive API from within a tile

Usage example:

const posts = await promiseHttpGet({
    href: 'https://apisrver.com/api/v1/posts',
    authz: 'signed'
})

async promiseHttpPost(...args)

Promise/async wrapper for osapi.http.get. Parameters are forwarded without change
Use it to access non-jive API from within a tile

Usage example:

const creationResponse = await promiseHttpPost({
    href: 'https://apisrver.com/api/v1/posts/create',
    authz: 'signed',
    headers : { 'Content-Type' : ['application/json'] },
    body: {
        title: 'My new blogpost',
        text: '<p>My new blogpost body</p>'
    }
})

async promiseRestGet(endpoint)

Promise/async wrapper for osapi.jive.core.get, which is an OSAPI endpoint for regular jive REST v3 requests.

Usage examples:

const viewer = await promiseRestGet('/people/@me')
const viewer = await promiseRestGet('/api/core/v3/people/@me')
// IMPORTANT! in the next case the host http://myserver.com will be completely thrown away and the 
// call will address the current jive instance. This is also true for all the next promiseRest... 
// functions
const viewer = await promiseRestGet('http://myserver.com/api/core/v3/people/@me')

async promiseRestPost(endpoint, options)

Promise/async wrapper for osapi.jive.core.post, which is an OSAPI endpoint for regular jive rest requests.

Usage example:

const batch = [/* your batch entries */]
const viewer = await promiseRestPost('/executeBatch', {
    type: "application/json",
    body: batch
})

async promiseRestDelete(endpoint)

Promise/async wrapper for osapi.jive.core.delete, which is an OSAPI endpoint for regular jive rest requests. Usage is the same as promiseRestGet

async promiseRestPut(endpoint, options)

Promise/async wrapper for osapi.jive.core.put, which is an OSAPI endpoint for regular jive rest requests. Usage is the same as promiseRestPost

async promiseRestBatch(entries, createBatchEntry, [options])

Performs a REST batch request. Bypasses jive's limitations of 25 entries per batch and 5 requests per 15 seconds

returns: Promise(Array) - array of results, whether they're elements, error reports, creation/deletion reports etc.

parameters:

  • entries - Array, based on which the batch request should be performed
  • createBatchEntry(entry, entryIndex, requestIndex) - function that creates a batch entry from entries array item. Should return a regular jive rest batch object (see example usage).
    • entry - single array item which has to be used to create batch element
    • entryIndex - index of this entry inside current batch request (since promiseRestBatch can abstract a few batch requests as one)
    • requestIndex - index of the batch request. This way usually request key is requestIndex + '.' + entryIndex
  • options - currently only one options is supported: maxEntries. In jive only 25 requests per batch is allowed, but you can make this number smaller (for example for performance reasons)

Usage examples:

// get content batch
const results = await promiseRestBatch([1001, 1003, 1005], (entry, entryIndex, requestIndex) => {
    return {
        key: requestIndex + '.' + entryIndex, // key by which you can identify the response
        request: {
            method: 'GET',
            endpoint: '/contents/' + entry
        }
    }
})
 
//content creation batch
const results = await promiseRestBatch([1, 2, 3], (entry, entryIndex, requestIndex) => {
    return {
        key: requestIndex + '.' + entryIndex,
        request: {
            method: 'POST',
            endpoint: '/contents',
            body: {
                "content": {
                    "type": "text/html",
                    "text": "<body><p>Some interesting text</p></body>"
                },
                "subject": "New Document " + entry,
                "type": "document"
            }
        }
    }
})

async promiseOsapiBatch(entries, createBatchEntry, [options])

Performs an OSAPI batch request. Bypasses jive's limitations of 25 entries per batch and 5 requests per 15 seconds

returns: Promise(Array) - array of results, whether they're elements, error reports, creation/deletion reports etc.

parameters:

  • entries - Array, based on which the batch request should be performed
  • createBatchEntry(entry, entryIndex, requestIndex) - function that creates a batch entry from entries array item. Should return an array of request id and osapi executable
    • entry - single array item which has to be used to create batch element
    • entryIndex - index of this entry inside current batch request (since promiseOsapiBatch can abstract a few batch requests as one)
    • requestIndex - index of the batch request. This way usually request key is requestIndex + '.' + entryIndex
  • options:
    • maxEntries - number of entries per batch. In jive only 25 requests per batch is allowed, but you can make this number smaller (for example for performance reasons)
    • shouldBatchContinue - function that should return false to stop batching at some point. Described in details in PostSortLoader.
      • param responseArray - results received in current batch
      • param results (optional) - all results, including current one

Usage examples:

// get content batch
const results = await promiseOsapiBatch([1001, 1003, 1005], (entry, entryIndex, requestIndex) => {
    return [
        requestIndex + '.' + entryIndex,
        osapi.jive.corev3.contents.get({id: entry})
    ]
})
 
// create content batch
const results = await promiseOsapiBatch([1, 2, 3], (entry, entryIndex, requestIndex) => {
    return [
        requestIndex + '.' + entryIndex,
        
        osapi.jive.corev3.contents.create({
            content: {
                "type": "text/html",
                "text": "<body><p>Some interesting text</p></body>"
            },
            "subject": "New Document " + entry,
            "type": "document"
        })
    ]
})

class CurrentPlace([<function> filter])

A class for getting the current place (sophisticated alternative to getContainerAsync)

Usage example:

const currentPlace = new CurrentPlace()
const place = await currentPlace.fetch() 

By default it returns not the jive place object, but the shortened version of the next format:

return {
  "id": rawPlace.placeID,
  "uri": rawPlace.resources.self.ref,
  "html": rawPlace.resources.html.ref,
  "name": unescapeHtmlEntities(rawPlace.name),
  "type": "place"
}

But if you want it another way, a filter function can be passed to a constructor:

const currentPlace = new CurrentPlace(rawPlace => {
    return rawPlace.resources.html.ref
})

or even this way, if you want a raw unfiltered place object

const currentPlace = new CurrentPlace(rawPlace => rawPlace)

ContinuousLoader

import {
    ContinuousLoader, 
    ContinuousLoadJiveREST, 
    ContinuousLoadJiveOSAPI
} from 'anrom-jive-app-tools/ContinuousLoader'

A set of classes for sequential requests and frontend-side content filtering

class ContinuousLoader(asyncFunction, filter, options)

General class for getting large amounts of data from APIs (or any other async interfaces) which do not provide sufficient filtering parameters. In other words, this is a tool for client-side filtering abstraction over regular APIs.

This class is not based on Jive API and can be used with any asynchronous functions but it has two descendants (see below) created especially for Jive's REST API and OSAPI.

params:

  • asyncFunction - ES2017 async function or any function that returns a Promise. Important: you should not pass Promise itself there, but a function that returns Promise when called.
  • filter - async/promise or regular function that is being called for each collection to define whether it's members pass to the final set or not (interface below)
  • options - other parameters (listed below)
    • required:
      • getNextAsyncFunc - function that returns new async/promise function for the next call. It can be based on previous async call's results.
    • optional (have defaults):
      • debug (false) - turns on console logging
      • targetCount (10) - Desired number of the items. Per one call of loadNext ContinuousLoader will make several requests and filter the results until the number is reached
      • maxTriesPerLoad (5) - Number of requests per single loadNext is by default limited to 5. You can set it to 0 to allow infinite calls (not safe for performance) or just pass another number
      • getError - calculates error type based on async/promise function response. By default just looks for response.error
      • getList - gets list of items from async/promise function response. By default looks for response.list

Argument functions' interfaces:

[async] filter(currentList, existingList)
should return: Array/Promise(Array); Filtered list of items

  • currentList - results collection received from the latest asyncFunction call
  • existingList - results from previous calls that have already passed this filter, but haven't been returned by loadNext yet (usually used to remove duplicates). Note that the filtered items which are already returned by loadNext are being deleted and will not be passed to this parameter again, so if you want to remove all the duplicates, you should check items against your target collection too.

Note: Since this function receives the array and returns the array, it can be used not only for filtering, but also for mapping (transforming) the array either before filtering, or after it. Also, this function can be async if needed, so you can make another async operations inside it, like calling for external services (use this option with caution).

getNextAsyncFunc(asyncFunctionResponse)
should return: async/promise function for the next call, analogical to the asyncFunction. If returns anything but a function (e.g. false) -it will be treated as a fact that there's no possibility to form a new request and the loader wil stop with "reason: source ended"

  • asyncFunctionResponse - response of the previous async/promise function

getError(asyncFunctionResponse)
should return: Error to display if there is one, false if no errors found.

  • asyncFunctionResponse - response of the previous async/promise function

getList(asyncFunctionResponse)
should return: Array of items

  • asyncFunctionResponse - response of the previous async/promise function

Methods

async ContinuousLoader.loadNext()
returns: Promise(Object)
Main loading function that tries to loads first/next stated number of items. Returning object contains two fields:

  • list - the resulting list of items
  • reason - text entry explaining why the polling stopped. Reason can be one of those:
    • "reached target count" - ContinuousLoader has found the desired count of items, but loadNext can be called one more time
    • "target count exists in pool" - there was no need in additional polling, the whole next "page" was found in an existing poll that has been fetched by previous loadNext
    • "max polls reached" - there can be more results, but ContinuousLoader made maxTriesPerLoad requests and target count haven't been reached
    • "source ended" - latest call returned 0 items, or getNextAsyncFunc returned not a function. Further polling makes no sense
    • "polling finished" - there been "source ended" response already, why do you still polling loadNext?

Usage examples

We will code a request for some abstract API that doesn't provide a server-side filtering by entity type:

// Let's first program our filter function
function filter(currentList, existingList){
    return currentList.filter((listItem, itemIndex, thisArray) => {
        
        // only items with type "document" match our selection
        if (listItem.type !== 'document') return false
        
        // also, we don't want duplicates (by ID) in this current list...
        if (thisArray.findIndex(dupItem => dupItem.id === listItem.id) !== itemIndex) return false
        
        // ...or in existing collection pool
        if (existingList.find(dupItem => dupItem.id === listItem.id)) return false
 
        // if all checks passed
        return true
    })
   
}
 
// Now set our first async API call function
function makeAPICall (){
    // this will return a Promise when "makeAPICall" is called
    return fetch('http://someapi.com/api/get/contents').then(response => response.json())
}
 
// And the function that tells loader how to make new API call 
// based on results from each previous one 
function getNextAsyncFunc(APIResponse){
    // form new URL minding the pagination from previous one's response
    const newURL = 'http://someapi.com/api/get/contents?startFrom=' 
        + (APIResponse.pageLength + APIResponse.startFrom)
    
    // returning new function
    // IMPORTANT! The returned value has to be an async function, not a promise!
    return () => fetch(newURL).then(response => response.json())
}
 
// Finally: initializing the class instance
const loader = new ContinuousLoader(makeAPICall, filter, {
    getNextAsyncFunc: getNextAsyncFunc
})
 
// Now we can get (hopefully) 10 document-typed items from that API with each call. We can get less 
// if the target count is not found in first 5 API calls, but we can set larger "maxTriesPerLoad", 
// or just set it to 0. Anyway, until items.reason is not "source ended" - it means there can be 
// more matching items in API source 
async function load(){
    const items = await loader.nextPage()
}

class ContinuousLoadJiveREST(asyncFunction, filter, [options])

A descendant class of ContinuousLoader, which has it's own getNextAsyncFunc, getError and getList implementations based on standard jive REST response, so you don't have to write it. It also has a set of new optional parameters:

  • loose (false) - true means that (list.length < itemsPerPage) doesn't mean list has ended. Useful when working with non-consistent /activities API, which sometimes can return number of items that is different from count parameter
  • method ("get") - you need to set this value if your default asyncFunction uses method different then "get" for internal getNextAsyncFunc implementation to work properly
  • createNextAsyncFunc - a creator of new asynchronous function based on jive API's "next" link

Usage Example:

// finds users that have Title === 'director'  
function filter(currentList){
    return currentList.filter(user => {
        return user.jive.profile && user.jive.profile.find(
            field => field.jive_label === 'Title' && field.value.toLowerCase() === 'director'
        )
    })
}
 
const loader = new ContinuousLoadJiveREST(() => promiseRestGet('/people'), filter)
 
const page1 = await loader.loadNext()

Argument functions' interfaces:
createNextAsyncFunc(nextLink, responseContent)
should return: async/promise function for the next call, analogical to the asyncFunction. The class has internal implementation of this function too, so you only have to use it if you have to do something else then just passing "next" link as a new request

  • nextLink - regular jive API's "next" link
  • responseContent - the whole response (if you need it)

class ContinuousLoadJiveOSAPI(asyncFunction, filter, options)

A descendant class of ContinuousLoader, which has it's own getList and getNextAsyncFunc implementations based on Jive's OSAPI response.

Usage Example:

// finds users that have Title === 'director'  
function filter(currentList){
    return currentList.filter(user => {
        return user.jive.profile && user.jive.profile.find(
            field => field.jive_label === 'Title' && field.value.toLowerCase() === 'director'
        )
    })
}
 
const loader = new ContinuousLoadJiveOSAPI(
    () => promiseOsapiRequest(osapi.jive.corev3.people.get()),
    filter
)
 
const page1 = await loader.loadNext()

PostSortLoader

import PostSortLoader from 'anrom-jive-app-tools/PostSortLoader'

Allows to load Jive content sorted by a parameter not supported by original Jive API. Attention: This loader is for Jive only at the moment!

class PostSortLoader(createSignatureRequest, createContentItemRequest, sortingFunction, options)

Class used to create a loader instance that abstracts post-load sorting.

More specifically, it relies on a suggestion that there's a way to reduce server load by requesting only fields that are necessary to sort items and then load them one-by-one in smaller portions.

params:

  • createSignatureRequest - func that should return single endpoint used in signature batch
    • param batchPageIndex - number of request starting from 0. Usually used to set startIndex param
  • createContentItemRequest - func that should return endpoint to a single content item
    • param signature - object with ID and field for sorting of the given item
  • sortingFunction - func that used for sorting, as usual for Array.sort
    • param itemA - used in standard Array.sort comparison
    • param itemB - used in standard Array.sort comparison
  • options - Object of additional parameters:
    • targetCount - initial number of elements to display after first load. Also evey other page given by loadNext() will be giving this number if no count parameter is passed. Default is 10.
    • batchNumber - how many pages of signature requests we want to get. Default is 5.
    • batchMaxEntries - how many requests formed by createSignatureRequest should one signature page have. Default is 25 (this is maximum for jive batch request)
    • shouldBatchContinue - function that works in a same way as in promiseOsapiBatch or promiseRestBatch.

Brief description of how it all calculated:
Let's say your createSignatureRequest creates a request for 100 content items and your batchNumber is 5 as per default. This means loader will make some number of batch requests to get 5 * x 100 = 500 content signatures. If your batchMaxEntries is 25, as per default, these 500 signatures will be received in one take, because 5 is lesser than 25. But if your batchNumber is 20 (imagine you want 2000 signatures) and your batchMaxEntries is 5 - loader will make 4 calls, 500 items each, to reach this number. Why is that useful?
Well, you can't know how many content items client does have. And each batch request takes some time to execute. You might find useful to take first 500, look at the last item and if it's empty - stop requesting for next, because by that empty request you know you reached end of content. That's what shouldBatchContinue does.

Methods

async PostSortLoader.loadNext(count)
returns: Promise(Object)
Main loading function that tries to loads first/next stated number of items. Returning object contains two fields:

  • list - the resulting list of items
  • reason - text entry explaining why the polling stopped. Reason can be one of those:
    • "reached target count" - PostSortLoader has found the desired count of items, but loadNext can be called one more time
    • "source ended" - no more signature items in the pool
    • "polling finished" - there been "source ended" response already, why do you still polling loadNext?

params:

  • count (optional) - size of a page to get.

Usage example

const loader = new PostSortLoader(
  batchPageIndex => url.format({
    pathname: '/contents',
    query: {
      filter: 'type(post)',
      count: 100, // 100 is max jive can give, so there's no use to ask for less
      startIndex: batchPageIndex * 100, //(0 for 0, 100 for 1, 200 for 2 etc.)
      //we only need publishDate to make a sorting and contentID to get element later
      fields: 'contentID,publishDate,-resources,-id'
    }           
  }),
 
  signature => url.format({
    pathname: '/contents/' + signature.contentID,
      query: {
        directive: 'silent' // don't increase view count 
      }
  }),
 
  // this function sorts each element by it's publishDate unix timestamp (the latest go to top)
  (a, b) => {
    if (moment.utc(a.publishDate).unix() > moment.utc(b.publishDate).unix()) return -1
    if (moment.utc(a.publishDate).unix() < moment.utc(b.publishDate).unix()) return 1
    return 0
  }, 
 
  {
    targetCount: 15, // we need 15 final results per page
    batchNumber: 20, //requesting  20 * 100 = 2000 signatures total
    batchMaxEntries: 5, // making it 5* 100 = 500 per page so that we could stop after each
 
    // batch only continues if last request in a batch is not empty
    shouldBatchContinue: responseArray => {
      const lastItem = responseArray[responseArray.length - 1]
      //last item returned empty - means there's no more data on the server
      return lastItem?.data?.list && lastItem.data.list.length !== 0
    }      
  }
)
 
const page = await loader.loadNext()

Migration Warnings

0.8.0-beta.14

If migrating from 0.8.0-beta.10 or later

utils/unescapeHtmlEntities now works properly again (filtering out html tags instead of turning them to quotes).

0.8.0-beta.10

If migrating from 0.8.0-beta.5 or later

utils/findContentImage and getContentImage now don't have the defaultImageURL parameter It's now up to the function user to make this fallback manually if received falsy value from the function

0.8.0-beta.8

If migrating from 0.8.0-beta.4 or later

  • currentPlace is no longer exported. Now you have to create instance of CurrentPlace manually
  • Removed support of 'map' option of ContinuousLoader

0.8.0-beta.2

If migrating from 0.5.0 or later

If you still want to use the deprecated promiseOsapiPollingRequest you have to reimport it from anrom-jive-app-tools/deprecated

0.7.4

Fixed critical error in tileProps/tileId - it just wasn't working since 0.3.0

0.5.0

  • promiseOsapiRequest, promiseHttpGet and promiseHttpPost are now throwing the entire response as error, not just response.error. In the same time, promiseRestRequest was always working that way so it doesn't need to be changed
  • Rename promiseRestRequest to promiseRestGet to avoid deprecation warning

0.3.0

tileProps: tileUrl, tilePath, tileId and parent are now made functions instead of just props (to support Gala, despite it's already cancelled)

Changelog

1.1.0

Features
  • Introduced sliceArray function. Contrary to splitArray it doesn't try to split array in even chunks of the given quantity, instead it slices it to unknown number of chunks of given length
  • promiseRestBatch and promiseOsapiBatch now have option parameter shouldBatchContinue, that, if defined, should take function, that will be called on each batch iteration and return false if batch should stop batching. Useful for loading unknown number of results
  • Introduced PostSortLoader

1.0.3

Fixes
  • Critical: ContinuousLoader use case was not handled: if there's something in results pool but not enough to give the targetCount AND source has already ended - loadNext was polling the same request as the latest one endlessly which resulted in response duplication.

1.0.1

Documentation updated

1.0.0

Release of all beta-channel changes starting from 0.8.0-beta.1

1.0.0-beta.6

Fixes
  • Fixed absence of String.prototype.includes polyfill

1.0.0-beta.5

Fixes
  • Fixed absence of Array.prototype.includes polyfill

1.0.0-beta.4

Fixes
  • Batch functions now properly recognize jive content entry point thanks to extractContent function that is also exported and free to use.

1.0.0-beta.1

Features
  • promiseBatch divided into promiseOsapiBatch and promiseRestBatch. Their work logic changed. If you use previous versions please refer to the commit to see changes

0.8.0-beta.17

Fixes
  • Critical error in tileProps/tileId - it just wasn't working since 0.3.0

0.8.0-beta.14

Fixes
  • dateUtils covered by tests and now checking for arguments' types
  • utils/unescapeHtmlEntities now works properly again (filtering out html tags instead of turning them to quotes)

0.8.0-beta.13

Fixes
  • Fixed jive date format for proper parsing

0.8.0-beta.12

Fixes
  • Better checking of aguments' types
  • Fixed moment2JiveDate (wasn't working before)

0.8.0-beta.10

Features
  • Jest testing is applied. Added parameters input test to some functions.
  • Changed the behavior of utils/abridge: now the last word isn't removed if the last symbol is whitespace or punctuation;
  • Changed behavior of utils/findContentImage: if image search mode is not 'api' and image is not found - 'api' method is used as a fallback. This can be turned off by passing 'false' as 3rd parameter to the function. getContentImage also gets the 'fallback' option in 'options' object parameter, defaulted to 'true'.
  • utils/findContentImage and getContentImage now don't have the defaultImageURL parameter. It's now up to the function user to make this fallback manually if received falsy value from the function
  • fetchPromise/promiseRest* functions now can detect "/api/core/v3/" in the URL and automatically remove it and everything before it (e.g. domain name)
  • unescapeHtmlEntities now also escapes '<' and '>' (this will be removed in the future)

0.8.0-beta.8

Features
  • Introduced utils/getImagelessHTML
  • Added node and npm version minimal requirements
  • Started API documentation
  • Removed Gala support
  • ContinuousLoader' option maxTriesPerLoad now can be set to 0 for indefinite loads
  • Added loose option to ContinuousLoader. When "loose == true" source of data isn't being treated as depleted just because current number of items is lesser than "itemsPerPage" parameter. Useful for jive's /activity API that can return data with page size different than sen in "count"
Fixes:
  • getContentImages 2nd argument (options) is now properly defaulted to an empty object
  • Removed dependency on webpack.externals for jQuery, jive, osapi and gadgets
  • now CurrentPlace is not being automatically instantiated and currentPlace is not exported
  • Removed support of 'map' option of ContinuousLoader

0.8.0-beta.6

Features
  • Introduced utils/jsonCopy and utils/isEmptyObject
Fixes
  • findContentImage now properly checks if contentItem.content.text exists

0.8.0-beta.5

Features
  • Introduced new functions of utils:
    • abridge
    • getCacheableImage
    • findContentImage
    • getContentImage

0.8.0-beta.4

Features
  • Introduced utils with the next functions:
    • pause
    • unescapeHtmlEntities
    • splitArray
  • Introduced dateUtils with the next functions:
    • jiveDateFormat
    • jiveDate2Moment
    • moment2JiveDate
    • jiveDate2TS
    • TS2JiveDate
  • Introduced fetchPromise/CurrentPlace class and it's instance fetchPromise/currentPlace
  • ContinuousLoader's "filter" argument can now be an async function
  • ContinuousLoader now knows when needed number of items already exists in the pool and doesn't make unneeded loads.
  • Added "map" option for ContinuousLoader (appeared to be a duplicate of "filter and removed later"!)
Fixes
  • promiseRestPost now supports second "options" object argument which members are added to 'v' and 'href'

0.8.0-beta.2

Features
  • Moved from babel-preset-latest to babel-preset-env
  • Deprecated use of promiseOsapiPollingRequest - it's now moved to anrom-jive-app-tools /deprecated
  • Introduced ContinuousLoader class to use instead of promiseOsapiPollingRequest

0.8.0-beta.1

Features
  • Moved to babel-runtime to avoid code duplication

0.7.4

Fixes
  • Critical error in tileProps/tileId - it just wasn't working since 0.3.0

0.7.1

Features
  • Introduced promiseRestDelete and promiseRestPut for fetchPromise
  • Introduced getContainerAsync for tileProps

0.6.2

Fixes
  • Fixed error in forEach polyfill in fetchPromise

0.6.0

Fixes
  • Fixed improper build (apparently not everything was working, probably with the "regenerator-runtime")

0.5.0

Features
  • Introduced first lazy version of promiseOsapiPollingRequest
  • Introduced promiseRestGet as a more properly-named copy of promiseRestRequest
  • Introduced promiseRestPost
  • Introduced promiseBatch
Fixes
  • promiseOsapiRequest, promiseHttpGet and promiseHttpPost are now throwing the entire response as error, not just response.error. In the same time, promiseRestRequest was always working that way so it doesn't need to be changed

0.4.0

Features
  • promiseHttpPost added to fetchPromise

0.3.0

Breaking
  • tileProps: tileUrl, tilePath, tileId and parent are now made functions instead of just props (to support Gala, despite it's already cancelled)
Critical Fixes
  • fetchPromise was not properly compiled
Fixes
  • fetchPromise added to index, so it could be imported not only directly from '/fetchPromise'

0.2.0

Features
  • Added fetchPromise sublibrary with the next functions:
    • promiseOsapiRequest
    • promiseRestRequest
    • promiseHttpGet

Readme

Keywords

Package Sidebar

Install

npm i anrom-jive-app-tools

Weekly Downloads

1

Version

1.1.0

License

MIT

Unpacked Size

200 kB

Total Files

30

Last publish

Collaborators

  • lerayne