Добавлено описание архитектуры

This commit is contained in:
Pavel Gnedov 2024-07-26 07:24:58 +07:00
parent 4a420dcf71
commit 8cae9076bd
16 changed files with 2467 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View file

@ -0,0 +1,252 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="380.13333mm"
height="110.25833mm"
viewBox="0 0 380.13334 110.25833"
version="1.1"
id="svg8"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
sodipodi:docname="Преобразования задач.svg"
inkscape:export-filename="/home/pavel/obsidian/Личные/Проекты/Монолитный Redmine Event Emitter/Документация/_resources/Преобразования задач.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2">
<marker
style="overflow:visible;"
id="marker1241"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Lend"
inkscape:isstock="true">
<path
transform="scale(1.1) rotate(180) translate(1,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path1239" />
</marker>
<marker
style="overflow:visible;"
id="marker1213"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Lend"
inkscape:isstock="true">
<path
transform="scale(1.1) rotate(180) translate(1,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path1211" />
</marker>
<marker
style="overflow:visible;"
id="Arrow2Lend"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Lend"
inkscape:isstock="true"
inkscape:collect="always">
<path
transform="scale(1.1) rotate(180) translate(1,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path918" />
</marker>
<marker
style="overflow:visible;"
id="Arrow2Mend"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"
inkscape:isstock="true">
<path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path924" />
</marker>
<rect
x="124.35417"
y="15.875"
width="105.83333"
height="31.75"
id="rect879" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="755.38262"
inkscape:cy="192.02171"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="true"
inkscape:window-width="1856"
inkscape:window-height="1051"
inkscape:window-x="64"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-text-baseline="true"
fit-margin-top="10"
lock-margins="true"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10">
<inkscape:grid
type="xygrid"
id="grid36"
originx="2.2125001"
originy="52.483332" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(2.2125,52.483332)">
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:85.6522;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="22.545856"
y="31.792259"
id="text30"
transform="translate(-6.6699219,-2.6880925)"><tspan
x="22.545856"
y="31.792259"><tspan
style="stroke-width:0.3">Чтение данных из </tspan></tspan><tspan
x="22.545856"
y="39.729758"><tspan
style="stroke-width:0.3">redmine </tspan><tspan
style="stroke-width:0.3">через </tspan><tspan
style="stroke-width:0.3">http api</tspan></tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect38"
width="100.54166"
height="31.75"
x="7.9375"
y="15.875" />
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="132.29167"
y="37.041668"
id="text865"><tspan
sodipodi:role="line"
id="tspan863"
x="132.29167"
y="37.041668"
style="stroke-width:0.3" /></text>
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6673;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="18.520834"
y="-7.9375"
id="text869"
transform="translate(-2.6464845,-23.812501)"><tspan
x="18.520834"
y="-7.9375"><tspan
style="stroke-width:0.3">Указание коллекции </tspan></tspan><tspan
x="18.520834"
y="2.970919e-07"><tspan
style="stroke-width:0.3">функций-</tspan></tspan><tspan
x="18.520834"
y="7.9375003"><tspan
style="stroke-width:0.3">преобразователей при </tspan></tspan><tspan
x="18.520834"
y="15.875"><tspan
style="stroke-width:0.3">инициализии приложения</tspan></tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:0.30000001;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:1.20000006,1.20000006;stroke-dashoffset:0"
id="rect871"
width="100.54166"
height="42.333332"
x="7.9375"
y="-42.333332" />
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:119.062;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="132.29167"
y="29.104166"
id="text875"><tspan
x="132.29167"
y="29.104166"><tspan
style="stroke-width:0.3">Выполнение всех функций-</tspan></tspan><tspan
x="132.29167"
y="37.041666"><tspan
style="stroke-width:0.3">преобразователей</tspan></tspan></text>
<text
xml:space="preserve"
id="text877"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect879);fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000;" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect883"
width="108.47916"
height="31.75"
x="124.35416"
y="15.875" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.30000001;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:1.20000006,1.20000006;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1;marker-end:url(#Arrow2Lend)"
d="m 108.47917,-23.8125 c 39.6875,0 63.5,10.583333 63.49999,39.6875"
id="path885"
sodipodi:nodetypes="cc" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1;marker-end:url(#marker1241)"
d="m 108.47917,31.75 h 15.875"
id="path887" />
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:113.771;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="261.9375"
y="29.10417"
id="text891"
transform="translate(-5.2916514)"><tspan
x="261.9375"
y="29.10417"><tspan
style="stroke-width:0.3">Дальнейшая работа с данными </tspan></tspan><tspan
x="261.9375"
y="37.04167"><tspan
style="stroke-width:0.3">(сохранение в </tspan><tspan
style="stroke-width:0.3">CouchDB</tspan><tspan
style="stroke-width:0.3">)</tspan></tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
id="rect893"
width="119.0625"
height="31.75"
x="248.70833"
y="15.875" />
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1;marker-end:url(#marker1213)"
d="m 232.83333,31.749999 h 15.875"
id="path895" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View file

@ -0,0 +1,462 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="265.65228mm"
height="517.26074mm"
viewBox="0 0 265.65229 517.26075"
version="1.1"
id="svg8"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
sodipodi:docname="Сохранение в CouchDB.svg"
inkscape:export-filename="/home/pavel/obsidian/Личные/Проекты/Монолитный Redmine Event Emitter/Документация/_resources/Сохранение в CouchDB.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="0.49497475"
inkscape:cx="530.06227"
inkscape:cy="959.12713"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="928"
inkscape:window-height="1051"
inkscape:window-x="992"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:snap-text-baseline="true"
showguides="false"
fit-margin-top="10"
lock-margins="true"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10">
<inkscape:grid
type="xygrid"
id="grid28"
originx="49.837479"
originy="-14.118424" />
<sodipodi:guide
position="60.420812,512.85835"
orientation="300,0"
id="guide873" />
<sodipodi:guide
position="160.96248,436.12918"
orientation="110.00002,0"
id="guide885" />
<sodipodi:guide
position="92.170813,433.48335"
orientation="0,105"
id="guide889" />
<sodipodi:guide
position="71.004145,420.25418"
orientation="165,0"
id="guide927" />
<sodipodi:guide
position="171.54582,420.25418"
orientation="60,0"
id="guide931" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(49.837477,-14.118424)">
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="-39.6875"
y="44.979164"
id="text857"><tspan
sodipodi:role="line"
x="-39.6875"
y="44.979164"
style="font-size:4.23333px;stroke-width:0.3"
id="tspan859">{</tspan><tspan
sodipodi:role="line"
x="-39.6875"
y="50.270828"
style="font-size:4.23333px;stroke-width:0.3"
id="tspan861"> &quot;_id&quot;: &quot;8ccc&quot;,</tspan><tspan
sodipodi:role="line"
x="-39.6875"
y="55.562489"
style="font-size:4.23333px;stroke-width:0.3"
id="tspan863"> &quot;_rev&quot;: &quot;0-abcd&quot;,</tspan><tspan
sodipodi:role="line"
x="-39.6875"
y="60.854149"
style="font-size:4.23333px;stroke-width:0.3"
id="tspan865"> &quot;field_a&quot;: &quot;value_a&quot;</tspan><tspan
sodipodi:role="line"
x="-39.6875"
y="66.145813"
style="font-size:4.23333px;stroke-width:0.3"
id="tspan867">}</tspan></text>
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="10.583333"
y="97.895836"
id="text877"><tspan
sodipodi:role="line"
id="tspan875"
x="10.583333"
y="97.895836"
style="stroke-width:0.3">Поток 1</tspan></text>
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6667;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="10.583333"
y="119.0625"
id="text881"
transform="translate(10.582682,5.2916667)"><tspan
x="10.583333"
y="119.0625"><tspan
style="stroke-width:0.3">Чтение документа из </tspan></tspan><tspan
x="10.583333"
y="127"><tspan
style="stroke-width:0.3">коллекции</tspan></tspan></text>
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="111.125"
y="97.895836"
id="text893"><tspan
sodipodi:role="line"
id="tspan891"
x="111.125"
y="97.895836"
style="stroke-width:0.3">Поток 2</tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6667;fill:#008000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="21.166666"
y="153.45833"
id="text901"
transform="translate(6.5094167e-4,3.9687551)"><tspan
x="21.166666"
y="153.45833"><tspan
style="font-size:4.23333px;fill:#008000;stroke-width:0.3">{
</tspan></tspan><tspan
x="21.166666"
y="158.74999"><tspan
style="font-size:4.23333px;fill:#008000;stroke-width:0.3"> id: 8ccc,
</tspan></tspan><tspan
x="21.166666"
y="164.04165"><tspan
style="font-size:4.23333px;fill:#008000;stroke-width:0.3"> rev: 0-abcd,
</tspan></tspan><tspan
x="21.166666"
y="169.3333"><tspan
style="font-size:4.23333px;fill:#008000;stroke-width:0.3"> field_a: value_a
</tspan></tspan><tspan
x="21.166666"
y="174.62496"><tspan
style="font-size:4.23333px;fill:#008000;stroke-width:0.3">}</tspan></tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none"
id="rect903"
width="5.2916665"
height="95.250008"
x="10.583333"
y="111.125" />
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6667;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="10.583333"
y="119.0625"
id="text881-6"
transform="translate(111.12435,44.979173)"><tspan
x="10.583333"
y="119.0625"><tspan
style="stroke-width:0.3">Чтение документа из </tspan></tspan><tspan
x="10.583333"
y="127"><tspan
style="stroke-width:0.3">коллекции</tspan></tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none"
id="rect903-5"
width="5.2916665"
height="140.22917"
x="111.125"
y="150.8125" />
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6667;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="10.583333"
y="119.0625"
id="text881-3"
transform="translate(10.582683,124.35415)"><tspan
x="10.583333"
y="119.0625"><tspan
style="stroke-width:0.3">Запись новой версии </tspan></tspan><tspan
x="10.583333"
y="127"><tspan
style="stroke-width:0.3">документа в </tspan><tspan
style="stroke-width:0.3">коллекцию</tspan></tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6667;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="21.166666"
y="153.45833"
id="text901-5"
transform="translate(6.4991001e-4,113.77082)"><tspan
x="21.166666"
y="153.45833"><tspan
style="font-size:4.23333px">{
</tspan></tspan><tspan
x="21.166666"
y="158.74999"><tspan
style="font-size:4.23333px"> id: 8ccc,
</tspan></tspan><tspan
x="21.166666"
y="164.04165"><tspan
style="font-size:4.23333px"> rev: 0-abcd,
</tspan></tspan><tspan
x="21.166666"
y="169.3333"><tspan
style="font-size:4.23333px"> </tspan><tspan
style="font-size:4.23333px;fill:#0000ff">field_a: success_value</tspan><tspan
style="font-size:4.23333px">
</tspan></tspan><tspan
x="21.166666"
y="174.62496"><tspan
style="font-size:4.23333px">}</tspan></tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none"
id="rect903-6"
width="5.2916665"
height="137.58333"
x="10.583333"
y="230.1875" />
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6667;fill:#008000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="21.166666"
y="333.375"
id="text1140"
transform="translate(6.4991001e-4,-26.458354)"><tspan
x="21.166666"
y="333.375"><tspan
style="fill:#008000;stroke-width:0.3">Успех. Документу </tspan></tspan><tspan
x="21.166666"
y="341.31251"><tspan
style="fill:#008000;stroke-width:0.3">внутри </tspan><tspan
style="fill:#008000;stroke-width:0.3">CouchDB </tspan><tspan
style="fill:#008000;stroke-width:0.3">будет </tspan></tspan><tspan
x="21.166666"
y="349.25001"><tspan
style="fill:#008000;stroke-width:0.3">проставлена новая </tspan></tspan><tspan
x="21.166666"
y="357.18751"><tspan
style="fill:#008000;stroke-width:0.3">ревизия - 1-</tspan><tspan
style="fill:#008000;stroke-width:0.3">bcde</tspan></tspan></text>
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="-39.6875"
y="29.104166"
id="text1260"><tspan
sodipodi:role="line"
id="tspan1258"
x="-39.6875"
y="29.104166"
style="stroke-width:0.3">Исходный документ:</tspan></text>
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;fill:#008000;stroke:none;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="21.166666"
y="148.16667"
id="text1268"><tspan
sodipodi:role="line"
id="tspan1266"
x="21.166666"
y="148.16667"
style="fill:#008000;stroke:none;stroke-width:0.3">Получили:</tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6667;fill:#008000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="21.166666"
y="153.45833"
id="text901-5-0"
transform="translate(6.4964419e-4,187.85415)"><tspan
x="21.166666"
y="153.45833"><tspan
style="font-size:4.23333px;fill:#008000">{
</tspan></tspan><tspan
x="21.166666"
y="158.74999"><tspan
style="font-size:4.23333px;fill:#008000"> id: 8ccc,
</tspan></tspan><tspan
x="21.166666"
y="164.04165"><tspan
style="font-size:4.23333px;fill:#008000"> rev: 1-bcde,
</tspan></tspan><tspan
x="21.166666"
y="169.3333"><tspan
style="font-size:4.23333px;fill:#008000"> </tspan><tspan
style="font-size:4.23333px;fill:#008000">field_a: success_value</tspan><tspan
style="font-size:4.23333px;fill:#008000">
</tspan></tspan><tspan
x="21.166666"
y="174.62496"><tspan
style="font-size:4.23333px;fill:#008000">}</tspan></tspan></text>
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6667;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="10.583333"
y="119.0625"
id="text881-3-9"
transform="translate(111.12433,285.74998)"><tspan
x="10.583333"
y="119.0625"><tspan
style="stroke-width:0.3">Запись новой версии </tspan></tspan><tspan
x="10.583333"
y="127"><tspan
style="stroke-width:0.3">документа в </tspan><tspan
style="stroke-width:0.3">коллекцию</tspan></tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6667;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="21.166666"
y="153.45833"
id="text901-5-3"
transform="translate(100.5423,275.16665)"><tspan
x="21.166666"
y="153.45833"><tspan
style="font-size:4.23333px">{
</tspan></tspan><tspan
x="21.166666"
y="158.74999"><tspan
style="font-size:4.23333px"> id: 8ccc,
</tspan></tspan><tspan
x="21.166666"
y="164.04165"><tspan
style="font-size:4.23333px"> rev: 0-abcd,
</tspan></tspan><tspan
x="21.166666"
y="169.3333"><tspan
style="font-size:4.23333px"> </tspan><tspan
style="font-size:4.23333px;fill:#0000ff">field_a: fail_value</tspan><tspan
style="font-size:4.23333px">
</tspan></tspan><tspan
x="21.166666"
y="174.62496"><tspan
style="font-size:4.23333px">}</tspan></tspan></text>
<rect
style="fill:none;stroke:#000000;stroke-width:0.300001;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none"
id="rect903-6-6"
width="5.2916665"
height="124.35415"
x="111.125"
y="391.58337" />
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6667;fill:#ff0000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="21.166666"
y="333.375"
id="text1140-0"
transform="translate(100.5423,134.93748)"><tspan
x="21.166666"
y="333.375"><tspan
style="fill:#ff0000">Fail. </tspan><tspan
style="fill:#ff0000">Ревизия не </tspan></tspan><tspan
x="21.166666"
y="341.31251"><tspan
style="fill:#ff0000">совпадает с предыдущим </tspan></tspan><tspan
x="21.166666"
y="349.25001"><tspan
style="fill:#ff0000">значением:
</tspan></tspan><tspan
x="21.166666"
y="357.18751"><tspan
style="fill:#ff0000">&quot;0-</tspan><tspan
style="fill:#ff0000">abcd&quot; != &quot;1-bcde&quot;.
</tspan></tspan><tspan
x="21.166666"
y="365.12501"><tspan
style="fill:#ff0000">Документ не будет </tspan></tspan><tspan
x="21.166666"
y="373.06251"><tspan
style="fill:#ff0000">обновлён.</tspan></tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;white-space:pre;inline-size:84.6667;fill:#008000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="21.166666"
y="153.45833"
id="text901-2"
transform="translate(100.54233,43.656245)"><tspan
x="21.166666"
y="153.45833"><tspan
style="font-size:4.23333px;fill:#008000;stroke-width:0.3">{
</tspan></tspan><tspan
x="21.166666"
y="158.74999"><tspan
style="font-size:4.23333px;fill:#008000;stroke-width:0.3"> id: 8ccc,
</tspan></tspan><tspan
x="21.166666"
y="164.04165"><tspan
style="font-size:4.23333px;fill:#008000;stroke-width:0.3"> rev: 0-abcd,
</tspan></tspan><tspan
x="21.166666"
y="169.3333"><tspan
style="font-size:4.23333px;fill:#008000;stroke-width:0.3"> field_a: value_a
</tspan></tspan><tspan
x="21.166666"
y="174.62496"><tspan
style="font-size:4.23333px;fill:#008000;stroke-width:0.3">}</tspan></tspan></text>
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;fill:#008000;stroke:none;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="121.70834"
y="187.85417"
id="text1268-6"><tspan
sodipodi:role="line"
id="tspan1266-1"
x="121.70834"
y="187.85417"
style="fill:#008000;stroke:none;stroke-width:0.3">Получили:</tspan></text>
<text
xml:space="preserve"
style="font-size:6.35px;line-height:normal;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono';text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;fill:#000000;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:bevel;stop-color:#000000"
x="-39.6875"
y="97.895836"
id="text1634"><tspan
sodipodi:role="line"
id="tspan1632"
x="-39.6875"
y="97.895836"
style="stroke-width:0.3">Время</tspan></text>
<path
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
d="m -34.395832,111.125 h 5.291666 v 402.16666 h 5.291666 l -7.937498,7.9375 -7.9375,-7.9375 h 5.291666 z"
id="path1636" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -0,0 +1,52 @@
# Общая структура
![](_resources/Архитектура%20-%20общая%20схема.png)
Основной код приложения написан на TypeScript и фреймворке NestJS (это такой Angular для backend-а)
Монолитное приложение включает в своём составе:
- основная функциональность
- кеш и промежуточный коллектор для данных на couchdb
- http api для внешних интеграций
- webhook-и и websocket-ы
- telegram-бот - есть возможность для реализации новых интеграций с другими чат-ботами
- cron-task-и для выполнения повторяющихся задач
- frontend server-side на простой шаблонизации
- frontend на react
- специальный плагин для lowcode платформы n8n
# Основной процесс
![](_resources/Архитектура%20-%20основной%20процесс.png)
Двигателем приложения служит redmine-issue-event-emitter - это основной процесс поступления данных в Pinkmine.
1. Подписка на источники событий - по email, через cron-таски, через rss. Позволяет обнаруживать факты изменений в задачах в redmine. ([подробнее](Как%20это%20работает/Как%20работают%20стратегии%20синхронизации%20redmine%20и%20pinkmine.md))
2. Redmine Issue Event Emitter - это очередь для синхронизации данных из redmine. На выход кладётся номер задачи, на выходе данные полученные с помощью redmine api. Очередь обеспечивает бережное отношение к боевому redmine, так как позволяет выполнять выгрузки не выходя за выставленный лимит обращений. ([подробнее про работу очереди](Как%20это%20работает/Как%20работает%20очередь%20загрузки%20задач.md) и [подробнее про загрузку задач из redmine](Как%20это%20работает/Как%20происходит%20загрузка%20задачи%20из%20Redmine.md))
3. Преобразование задачи - это набор вспомогательных обработчиков. Позволяет при получении данных произвести попутно полезные изменения, например, привести текстовые теги к массиву, определить текущего ответственного за задачу, обнаружить в описании или в комментариях ссылки на gitlab на MR-ы, в т.ч. можно реализовать специализированный обработчик. ([подбронее](./Как%20это%20работает/Как%20происходит%20преобразование%20задачи.md))
4. Сохранение задачи в кеш в CouchDB ([подробнее](./Как%20это%20работает/Сохранение%20задачи%20в%20кеш%20CouchDB.md))
5. Анализ изменений в задаче - это сравнение предыдущего состояния задачи с новым. Позволяет организовать процессы связанные с жизненным циклом самой задачи и, например, разослать уведомления с учётом происходящих изменений ([подробнее](./Как%20это%20работает/Анализ%20изменений%20в%20задаче.md))
6. Сохранение событий изменения в журнал - вспомогательная коллекция данных ([подробнее](./Как%20это%20работает/Анализ%20изменений%20в%20задаче.md))
# Точки расширения
Платформа Pinkmine предполагает в т.ч. реализацию конкретных задач в рамках основного кода. Допускается реализация специализированных внутренних сервисов.
В рамках ядра redmine-issue-event-emitter:
- новые стратегии получения задач из Redmine
- специализированные обработчики задач для отдельных проектов
- интеграция событий в новые процессы - чат-боты или простая передача данных с помощью webhook-ов и websocket-ов
В рамках расширенной платформы pinkmine:
Благодаря CouchDB стало возможным предоставить api для поиска задач с более гибкими условиями для выборки чем во встроенном в redmine api. Можно использовать в выборках комбинированные выражения через "и"/"или". Богатый набор операторов - "есть значение", "нет значения", "больше", "меньше", "равно", "поиск по регулярному выражению". Для поиска можно пользоваться расширенными данными.
CouchDB позволяет выгружать для работы и анализа больший объём данных чем основной api redmine.
Webhook-и и websocket-ы могут сделать api ещё более гибким.
В рамках реализованного telegram chat бота можно дописывать логику работы под узкоспециализированные задачи.
С помощью фреймворка NestJS можно сделать сервисы для решения узких задач. Например, сделать cron-task-у с аналитической выгрузкой с сохранением промежуточных результатов во вспомогательную коллекцию в CouchDB и предостатить уже к ней доступ через http api и сделать человеко-читаемое представление с помощью шаблона.

View file

@ -0,0 +1,64 @@
Как было описано в документе [Сохранение задачи в кеш CouchDB](./Сохранение%20задачи%20в%20кеш%20CouchDB.md) специфика работы с данными из issue такова что есть два состояния - предыдущее и текущее состояние.
Значит есть возможность сравнения двух состояний и построение дополнительной логики на основе сравнения.
Это позволило реализовать рассылку уведомлений при обнаружении в задачах изменений.
Функции сохранения данных в CouchDB с вычислением изменений реализована в `libs/event-emitter/src/issue-cache-writer/redmine-issues-cache-writer.service.ts`.
Затем с помощью пайплайнов rxjs реализован дополнительный анализ изменений:
1. `src/notifications/personal-notifications.service.ts` - поиск упоминаний пользователей redmine в комментариях для пересылки личных уведомлений в чат-бот
2. `src/notifications/status-change-notifications.service.ts` - поиск изменений статусов для рассылки уведомлений согласно изменениям ответственного за дальнейшую работу над задачей
3. `src/changes-cache-writer/changes-cache-writer.service.ts` - сохранение изменений в дополнительный журнал активностей в коллекцию в CouchDB
Сам пайплайн rxjs определяется в инициализирующей функции в `src/app.module.ts`.
Для правильности работы функций анализа нужно выполнить правильно конфигугирование.
Статусы используемые в вашем экземпляре redmine определяются в конфигурационном файле `configs/redmine-statuses-config.jsonc`. Они имеет следующий вид:
```json
[
{
"id": 1,
"name": "New"
},
{
"id": 2,
"name": "In Progress"
},
{
"id": 3,
"name": "Closed",
"is_closed": true
}
// ...
]
```
Идентификаторы и name должны соответствовать статусам настроенным в основном redmine.
Правила для определения изменений в статусах определяются с помощью конфигурационного файла `configs/redmine-status-changes-config.jsonc`. Он имеет следующий вид:
```json
[
{
"default": false,
"from": "New",
"to": "In Progress",
"messages": [
{
"recipient": "<field_name>",
// Handlebars - template engine
"changes_message": "{{qa.name}} got issue #{{issue_id}} after development {{dev.name}}",
"notification_message": "<a href=\"{{ issue_url }}\">{{ issue_tracker }} #{{ issue_id }}</a> {{ issue_subject }}:\n{{dev.name}} finished development. You can test issue.\n\n{{journal_note}}"
}
// ...
]
}
// ...
]
```
Это набор правил для формирования уведомлений и сообщений для журнала изменений. Поля "from" и "to" - указывают на возможные статусы задачи. "messages" - это набор сообщений. Тут можно определить варианты сообщений для различных получателей задаваемых полем "recipient", а тексты сообщений с помощью шаблонизатора [Handlebars](https://handlebarsjs.com).

View file

@ -0,0 +1,6 @@
Это обращение к стандартной функции через http api по следующему шаблону:
`<redmine_url>/issues/<issue_number>.json?include=children,journals,relations`
Подробное описание параметров api можно найти в официальной фокументации на странице [Rest_Issues](https://www.redmine.org/projects/redmine/wiki/Rest_Issues)

View file

@ -0,0 +1,41 @@
Для разрешения некоторых проблем напрашивалось попутно после получения данных об issue из redmine делать преобразования
Примеры:
1. Преобразовать дату+вермя из текстового формата в числовой для возможности фильтрации данных с помощью сравнивающих операторов - больше и меньше.
2. Привести текстовое значение тегов к массиву для фильтрации данных с помощью оператора "$in".
3. Можно извлечь из описания и комментариев ссылки на MR-ы в gitlab-е.
4. Определить текущего ответственного за задачу по правилам соответствия статусу одному из полей - "Назначено", "Code Reviewer", "Quality Assurance".
Через конфигурационный файл уже не решить настройку, т.к. за преобразования задач отвечают функции - а если точнее то классы с реализацией интерфейса `IssueEnhancerInterface`. Это делается в коде проекта Pinkmine. Примеры реализаций в файлах:
- `libs/event-emitter/src/issue-enhancers/timestamps-enhancer.ts` - преобразует дату+время из текстового формата в числовой
- `libs/event-emitter/src/issue-enhancers` - в папке ещё несколько универсальных примеров (не зависящих от экземпляра redmine)
- `src/issue-enhancers` - в папке лежат дополнительные примеры с функциями уже привязанными к рабочему redmine и специфичные для рабочих проектов
Чтобы эти функции отрабатывали налету после получения данных из redmine и перед сохранением в кеш, то нужно при начальной инициализации приложения (`src/app.module.ts` > `AppModule.onModuleInit`) указать набор реализаций `IssueEnhancerInterface`. Упрощённый код, отражающий суть:
```ts
export class AppModule {
// ...
onModuleInit() {
// ...
this.enhancerService.addEnhancer([
this.timestampEnhancer,
this.customFieldsEnhancer,
this.currentUserEnhancer,
this.issueUrlEnhancer,
this.categoryMergeToTagsEnhancer,
this.calendarEnhancer,
]);
// ...
}
// ...
}
```
Схематически процесс можно проиллюстрировать так:
![Преобразования задач](../_resources/Преобразования%20задач.png)
Выполнение преобразований данных до сохранения в кеш в CouchDB позволяет в последствии использовать дополнительные данные для выборки задач

View file

@ -0,0 +1,22 @@
Это стандартный паттерн:
1. берём максимум N номеров задач раз в M секунд,
2. передаём их в функцию для синхронизаицации
1. выгружаем данные о задаче из redmine api
2. пишем полученные данные во внутренний кеш
Очередь нужна для бережного отношения к боевому redmine, потому что не загрузит CPU на сервере сотнями одновременных параллельных запросов.
Настройка очереди производится в конфигурационном файле `configs/issue-event-emitter-config.jsonc` в секции `issueChangesQueue`:
```json
{
// ...
"issueChangesQueue": {
"updateInterval": 5000, // 5 sec
"itemsLimit": 3
}
// ...
}
```

View file

@ -0,0 +1,106 @@
# Стратегия по email
![](../_resources/Папка%20в%20почте%20с%20уведомлениями%20из%20Redmine.png)
1. В задаче в redmine делается любое изменение - смена статуса, обновление описания, добавление нового комментария.
2. Redmine отправляет сообщение о факте обновления задачи по email
3. Pinkmine читает новые письма протоколу imap, находит в заголовках номер задачи с помощью регулярного выражения
4. Pinkmine помещает номер задачи в очередь для выполнения обновления
Параметры работы стратегии задаются в конфигурационном файле `configs/issue-event-emitter-config.jsonc` в секции `mailListener`:
```json
{
// ...
"mailListener": {
// регулярное выражение для определения номера задачи в заголовке письма:
"issueNumberParser": "\\b(?<=#)\\d+\\b",
// параметры для доступа к почте через протокол imap:
"imapSimpleConfig": {
"imap": {
"user": "",
"password": "",
"host": "",
"port": 143,
// tls: true,
"autotls": "always",
"authTimeout": 5000
}
},
// интервал через который происходит проверка почты:
"updateInterval": 180000, // 3 min
// имя папки в которой следует искать входящие письма
"boxName": "INBOX"
},
// ...
}
```
# Стратегия через cron-таски
![Преднастроенный фильтр в Redmine](../_resources/Преднастроенный%20фильтр%20в%20Redmine.png)
1. Pinkmine загружает данные из преднастроенных query-запросов в redmine в формате csv. Выбирает данные по двум полям - id задачи и "обновлено" (дата+время обновления задачи)
2. Pinkmine проверяет какие задачи в своём кеше (CouchDB) уже успели устареть
3. Номера устаревших задач кладёт в очередь для дальнейшего обновления
Параметры работы стратегии задаются в конфигурационном файле `configs/issue-event-emitter-config.jsonc` в секциях `csvListener` и `rootIssueListener`:
```json
{
// ...
// таски для синхронизации по преднастроенным запросам:
"csvListener": {
"tasks": [
{
// расписание для выполнения задачи (синтаксис crontab)
"schedule": "* * * * *",
// поле в csv-файле с датой и временем обновления задачи
"updatedAtFieldName": "Обновлено",
// формат даты и времени
"dateTimeFormat": "dd.MM.yyyy HH:mm",
// ссылки на предстароенные запросы выборки задач в формате csv
"csvLinks": [
"https://<redmine_host>/projects/proj/issues.csv?utf8=%E2%9C%93&query_id=2"
]
}
]
},
// таски для синхронизации корневых и суммирующих задач:
"rootIssueListener": {
"tasks": [
{
// расписание для выполнения задачи (синтаксис crontab)
"schedule": "15 6,12 * * *",
// номера задач
"rootIssues": [
1,
3,
7,
11
]
}
]
},
// ...
}
```
Эта стратегия позволяет догружать данные, которые не пришли по email рассылке: закрытые задачи, shady-mode, изменение структуры дерева при смене привязки к родительской задаче и т.п.

View file

@ -0,0 +1,32 @@
CouchDB - это nosql субд. Основная концепция CouchDB - это коллекции данных (json) с возможностью гибкого поиска по ним.
Важная особенность CouchDB - это отсутствие механизма транзакций. Гарантия согласованности записи обеспечивается с помощью контроля ревизии.
Документы в CouchDB имеют версионирование, аналогичное тому, как это было бы в обычной системе контроля версий, такой как Subversion. Если вы хотите изменить значение в документе, вы создаете полностью новую версию этого документа и сохраняете ее поверх старой. После выполнения этого вы получите две версии одного и того же документа, одну старую и одну новую.
Как это обеспечивает улучшение по сравнению с блокировками? Рассмотрим набор запросов, желающих получить доступ к документу. Первый запрос считывает документ. Пока он обрабатывается, второй запрос изменяет документ. Поскольку второй запрос включает совершенно новую версию документа, CouchDB может просто добавить его в базу данных, не дожидаясь завершения запроса на чтение.
Когда третий запрос захочет прочитать тот же документ, CouchDB укажет ему на новую версию, которая только что была написана. В течение всего этого процесса первый запрос все еще может читать исходную версию.
Запрос на чтение всегда будет отображать самый последний снимок вашей базы данных на момент начала запроса.
![Сохранение в CouchDB](../_resources/Сохранение%20в%20CouchDB.png)
Redmine Issue Event Emitter всегда для записи нового состояния задачи должен прочитать предыдущее состояние. Это обязательно нужно для корректного определения последней ревизии. Одновременно для дальнейшего анализа станет доступно два состояния - предыдущее и текущее. Благодаря этой особенности работы с CouchDB можно проводить дополнительный [анализ произошедших в задаче изменений](./Анализ%20изменений%20в%20задаче.md).
Настройка доступа к CouchDB делается в конфигурационном файле `configs/issue-event-emitter-config.jsonc` в секции `couchDb`:
```json
{
// ...
"couchDb": {
"url": "http://admin:password@localhost:5984",
"dbs": {
"users": "redmine_users",
"issues": "redmine_issues",
"dashboards": "dashboards"
}
}
// ...
}
```