First let's define a class which extends
FlowElementBase
(and partially implements
IFlowElement
). This has the type arguments of
IStarSignData
- the interface extending
element data which will be added to the
flow data, and
IElementPropertyMetaData
- as we only need the standard metadata for
element properties.
This needs a constructor matching the FlowElementBase
class. So it takes a logger, and an element data factory which will be used to construct an IStarSignData
:
public class SimpleFlowElement : FlowElementBase<IStarSignData, IElementPropertyMetaData>
{
public SimpleFlowElement(
ILogger<FlowElementBase<IStarSignData, IElementPropertyMetaData>> logger,
Func<IPipeline, FlowElementBase<IStarSignData, IElementPropertyMetaData>, IStarSignData> elementDataFactory)
: base(logger, elementDataFactory)
{
Init();
}
The Init
method in this example will simply initialize a list of star signs with the start and end dates of each star sign and add each to a list of a new class named StarSign
which has the following simple implementation:
internal class StarSign
{
public StarSign(string name, DateTime start, DateTime end)
{
Name = name;
Start = start.AddYears(-start.Year + 1);
End = end.AddYears(-end.Year + 1);
}
public string Name { get; private set; }
public DateTime Start { get; private set; }
public DateTime End { get; private set; }
}
Note that the year of the start and end date are both set to 1, as the year should be ignored, but the year 0 cannot be used in a DateTime
.
The new Init
method looks like this:
private void Init()
{
var starSigns = new List<StarSign>();
foreach (var starSign in _starSignData)
{
starSigns.Add(new StarSign(
starSign[0],
DateTime.ParseExact(starSign[1], @"yy/MM/dd", CultureInfo.InvariantCulture),
DateTime.ParseExact(starSign[2], @"yy/MM/dd", CultureInfo.InvariantCulture)));
}
_starSigns = starSigns;
}
Now the abstract methods can be implemented to create a functional flow element.
public class SimpleFlowElement : FlowElementBase<IStarSignData, IElementPropertyMetaData>
{
public SimpleFlowElement(
ILogger<FlowElementBase<IStarSignData, IElementPropertyMetaData>> logger,
Func<IPipeline, FlowElementBase<IStarSignData, IElementPropertyMetaData>, IStarSignData> elementDataFactory)
: base(logger, elementDataFactory)
{
Init();
}
private IList<StarSign> _starSigns;
private static string[][] _starSignData = {
new string[3] {"Aries", "01/03/21", "01/04/19"},
new string[3] {"Taurus","01/04/20","01/05/20"},
new string[3] {"Gemini","01/05/21","01/06/20"},
new string[3] {"Cancer","01/06/21","01/07/22"},
new string[3] {"Leo","01/07/23","01/08/22"},
new string[3] {"Virgo","01/08/23","01/09/22"},
new string[3] {"Libra","01/09/23","01/10/22"},
new string[3] {"Scorpio","01/10/23","01/11/21"},
new string[3] {"Sagittarius","01/11/22","01/12/21"},
new string[3] {"Capricorn","01/12/22","01/01/19"},
new string[3] {"Aquarius","01/01/20","01/02/18"},
new string[3] {"Pisces","01/02/19","01/03/20"}
};
private void Init()
{
var starSigns = new List<StarSign>();
foreach (var starSign in _starSignData)
{
starSigns.Add(new StarSign(
starSign[0],
DateTime.ParseExact(starSign[1], @"yy/MM/dd", CultureInfo.InvariantCulture),
DateTime.ParseExact(starSign[2], @"yy/MM/dd", CultureInfo.InvariantCulture)));
}
_starSigns = starSigns;
}
public override string ElementDataKey => "starsign";
public override IEvidenceKeyFilter EvidenceKeyFilter =>
new EvidenceKeyFilterWhitelist(new List<string>() { "date-of-birth" });
public override IList<IElementPropertyMetaData> Properties =>
new List<IElementPropertyMetaData>()
{
new ElementPropertyMetaData(this, "starsign", typeof(string), true)
};
protected override void ProcessInternal(IFlowData data)
{
StarSignData starSignData = (StarSignData)data.GetOrAdd(ElementDataKey, CreateElementData);
if (data.TryGetEvidence("date-of-birth", out DateTime dateOfBirth))
{
var monthAndDay = new DateTime(1, dateOfBirth.Month, dateOfBirth.Day);
foreach (var starSign in _starSigns)
{
if (monthAndDay > starSign.Start &&
monthAndDay < starSign.End)
{
starSignData.StarSign = starSign.Name;
break;
}
}
}
else
{
starSignData.StarSign = "Unknown";
}
}
protected override void ManagedResourcesCleanup()
{
}
protected override void UnmanagedResourcesCleanup()
{
}
}
}
First let's define a class which extends FlowElementBase
(which partially implements FlowElement
). This has the type arguments of StarSignData
- the interface extending element data which will be added to the flow data, and ElementPropertyMetaData
- as we only need the standard metadata for element properties.
This needs a constructor matching the FlowElementBase
class. So it takes a logger, and an element data factory which will be used to construct a StarSignData
:
public class SimpleFlowElement extends FlowElementBase<StarSignData, ElementPropertyMetaData> {
public SimpleFlowElement(
Logger logger,
ElementDataFactory<StarSignData> elementDataFactory) {
super(logger, elementDataFactory);
init();
}
The init
method in this example will simply initialize a list of star signs with the start and end dates of each star sign and add each to a list of a new class named StarSign
which has the following simple implementation:
public class StarSign {
private final Calendar end;
private final Calendar start;
private final String name;
public StarSign(String name, String start, String end) {
this.name = name;
this.start = Calendar.getInstance();
String[] startDate = start.split("/");
this.start.set(
0,
Integer.parseInt(startDate[1]) - 1,
Integer.parseInt(startDate[0]));
this.end = Calendar.getInstance();
String[] endDate = end.split("/");
this.end.set(
0,
Integer.parseInt(endDate[1]) - 1,
Integer.parseInt(endDate[0]));
}
public String getName() {
return name;
}
public Calendar getStart() {
return start;
}
public Calendar getEnd() {
return end;
}
}
Note that the year of the start and end date are both set to 0, as the year should be ignored.
The new init
method looks like this:
private void init() {
List<StarSign> starSigns = new ArrayList<>();
for (String[] starSign : starSignData) {
starSigns.add(new StarSign(
starSign[0],
starSign[1],
starSign[2]));
}
this.starSigns = starSigns;
}
Now the abstract methods can be implemented to create a functional flow element.
public class SimpleFlowElement extends FlowElementBase<StarSignData, ElementPropertyMetaData> {
public SimpleFlowElement(
Logger logger,
ElementDataFactory<StarSignData> elementDataFactory) {
super(logger, elementDataFactory);
init();
}
private static final String[][] starSignData = {
{"Aries","21/03","19/04"},
{"Taurus","20/04","20/05"},
{"Gemini","21/05","20/06"},
{"Cancer","21/06","22/07"},
{"Leo","23/07","22/08"},
{"Virgo","23/08","22/09"},
{"Libra","23/09","22/10"},
{"Scorpio","23/10","21/11"},
{"Sagittarius","22/11","21/12"},
{"Capricorn","22/12","19/01"},
{"Aquarius","20/01","18/02"},
{"Pisces","19/02","20/03"}
};
private List<StarSign> starSigns;
private void init() {
List<StarSign> starSigns = new ArrayList<>();
for (String[] starSign : starSignData) {
starSigns.add(new StarSign(
starSign[0],
starSign[1],
starSign[2]));
}
this.starSigns = starSigns;
}
@Override
protected void processInternal(FlowData data) throws Exception {
StarSignDataInternal elementData = (StarSignDataInternal)data.getOrAdd(getElementDataKey(),getDataFactory());
TryGetResult<Date> date = data.tryGetEvidence("date-of-birth", Date.class);
if (date.hasValue()) {
Calendar dob = Calendar.getInstance();
dob.setTime(date.getValue());
Calendar monthAndDay = Calendar.getInstance();
monthAndDay.set(
0,
dob.get(Calendar.MONTH),
dob.get(Calendar.DATE));
for (StarSign starSign : starSigns) {
if (monthAndDay.compareTo(starSign.getStart()) >= 0 &&
monthAndDay.compareTo(starSign.getEnd()) <= 0) {
elementData.setStarSign(starSign.getName());
break;
}
}
}
else
{
elementData.setStarSign("Unknown");
}
}
@Override
public String getElementDataKey() {
return "starsign";
}
@Override
public EvidenceKeyFilter getEvidenceKeyFilter() {
return new EvidenceKeyFilterWhitelist(
Collections.singletonList("date-of-birth"),
String.CASE_INSENSITIVE_ORDER);
}
@Override
public List<ElementPropertyMetaData> getProperties() {
return Collections.singletonList(
(ElementPropertyMetaData) new ElementPropertyMetaDataDefault(
"starsign",
this,
"starsign",
String.class,
true));
}
@Override
protected void managedResourcesCleanup() {
}
@Override
protected void unmanagedResourcesCleanup() {
}
}
First let's define a class which extends
flowElement
:
class AstrologyFlowElement extends FlowElement
{
Now the abstract methods can be implemented to create a functional flow element.
class AstrologyFlowElement extends FlowElement
{
public $dataKey = "astrology";
public function processInternal($FlowData)
{
$result = [];
$dateOfBirth = $FlowData->evidence->get("query.dateOfBirth");
if ($dateOfBirth) {
$dateOfBirth = explode("-", $dateOfBirth);
$month = $dateOfBirth[1];
$day = $dateOfBirth[2];
$result["starSign"] = getStarSign($month, $day);
}
$result["getLatitude"] = "navigator.geolocation.getCurrentPosition(function(position) {
document.cookie = \"latitude=\" + position.coords.latitude;
loadHemisphere();
});";
$latitude = $FlowData->evidence->get("cookie.latitude");
if ($latitude) {
$result["hemisphere"] = $latitude > 0 ? "Northern" : "Southern";
}
$data = new ElementDataDictionary($this, $result);
$FlowData->setElementData($data);
}
public $properties = array(
"starSign" => array(
"type" => "string",
"description" => "the user's starsign"
),
"hemisphere" => array(
"type" => "string",
"description" => "the user's hemisphere"
),
"getLatitude" => array(
"type" => "javascript",
"description" => "JavaScript used to get a user's latitude"
)
);
public function getEvidenceKeyFilter()
{
return new BasicListEvidenceKeyFilter(["cookie.latitude", "query.dateOfBirth"]);
}
}
First let's define a class which extends
flowElement
.
This needs a constructor matching the flowElement
class which initializes the element:
// Astrology flowElement
class Astrology extends FiftyOnePipelineCore.FlowElement {
constructor () {
super(...arguments);
// datakey used to categorise data coming back from
// this flowElement in a pipeline
this.dataKey = 'astrology';
// A filter (in this case a basic list) stating which evidence the
// flowElement is interested in, in this case a query string
this.evidenceKeyFilter = new FiftyOnePipelineCore
.BasicListEvidenceKeyFilter(['query.dateOfBirth']);
// The properties list includes extra information about
// the properties available from a flowElement
this.properties = {
starSign: {
type: 'string',
description: "the user's starsign"
}
};
}
Now the abstract methods can be implemented to create a functional flow element.
//! [constructor]
// Astrology flowElement
class Astrology extends FiftyOnePipelineCore.FlowElement {
constructor () {
super(...arguments);
// datakey used to categorise data coming back from
// this flowElement in a pipeline
this.dataKey = 'astrology';
// A filter (in this case a basic list) stating which evidence the
// flowElement is interested in, in this case a query string
this.evidenceKeyFilter = new FiftyOnePipelineCore
.BasicListEvidenceKeyFilter(['query.dateOfBirth']);
// The properties list includes extra information about
// the properties available from a flowElement
this.properties = {
starSign: {
type: 'string',
description: "the user's starsign"
}
};
}
//! [constructor]
// Internal processing function
processInternal (flowData) {
const result = {};
// Get the date of birth from the query string
// (submitted through a form on the client side)
let dateOfBirth = flowData.evidence.get('query.dateOfBirth');
if (dateOfBirth) {
dateOfBirth = dateOfBirth.split('-');
const month = parseInt(dateOfBirth[1]);
const day = parseInt(dateOfBirth[2]);
result.starSign = getStarSign(month, day);
}
// Save the data into an extension of the elementData class
// (in this case a simple dictionary subclass)
const data = new FiftyOnePipelineCore.ElementDataDictionary({
flowElement: this, contents: result
});
// Set this data on the flowElement
flowData.setElementData(data);
}
}