diff --git a/Gemfile b/Gemfile index 54b6072f..237ba38d 100644 --- a/Gemfile +++ b/Gemfile @@ -22,6 +22,7 @@ group :jekyll_plugins do gem 'jekyll-sitemap' gem 'jekyll-feed' gem 'jekyll-seo-tag' + gem 'jekyll-tabs' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem diff --git a/Gemfile.lock b/Gemfile.lock index ec16d468..fda3e9ec 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -158,6 +158,8 @@ GEM jekyll-sitemap (1.4.0) jekyll (>= 3.7, < 5.0) jekyll-swiss (1.0.0) + jekyll-tabs (1.2.1) + jekyll (>= 3.0, < 5.0) jekyll-theme-architect (0.2.0) jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) @@ -271,6 +273,7 @@ DEPENDENCIES jekyll-feed jekyll-seo-tag jekyll-sitemap + jekyll-tabs tzinfo-data webrick (~> 1.7) diff --git a/_config.yml b/_config.yml index a43d8944..85351614 100644 --- a/_config.yml +++ b/_config.yml @@ -29,6 +29,7 @@ highlighter: rouge # theme: minima plugins: - jekyll-feed + - jekyll-tabs defaults: - scope: diff --git a/_data/nav.yml b/_data/nav.yml index 3c3d3a3f..ebaf403f 100644 --- a/_data/nav.yml +++ b/_data/nav.yml @@ -1,5 +1,5 @@ - name: Getting Started - link: /ontology/start.html + link: /ontology/get-started.html - name: Releases submenuitems: - name: Latest Release diff --git a/_includes/header.html b/_includes/header.html index 02a04e4b..af7ae793 100644 --- a/_includes/header.html +++ b/_includes/header.html @@ -18,6 +18,8 @@ + + diff --git a/_layouts/default.html b/_layouts/default.html index 8193a205..771789ea 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -16,6 +16,7 @@

{{ content }} +
{% include footer.html %} diff --git a/assets/css/main.css b/assets/css/main.css index 23cc7f9c..895b3dec 100755 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -37,3 +37,21 @@ footer.footer { padding-top: 4rem; padding-bottom: 4rem; } + +table { + width: 100%; + max-width: 100%; + margin-bottom: 1rem; + background-color: transparent; + border-collapse: collapse; + color: #333; +} + +table th { + font-weight: bold; + border-bottom: 2px solid #ccc; +} + +table tr { + border-bottom: 1px solid #ccc; +} \ No newline at end of file diff --git a/assets/css/tabs.css b/assets/css/tabs.css new file mode 100644 index 00000000..5f0646bd --- /dev/null +++ b/assets/css/tabs.css @@ -0,0 +1,70 @@ +.tab { + display: flex; + flex-wrap: wrap; + margin-left: -20px; + padding: 0; + list-style: none; + position: relative; +} + +.tab > * { + flex: none; + padding-left: 20px; + position: relative; +} + +.tab > * > a { + display: block; + text-align: center; + padding: 9px 20px; + color: #999; + border-bottom: 2px solid transparent; + border-bottom-color: transparent; + font-size: 12px; + text-transform: uppercase; + transition: color .1s ease-in-out; + line-height: 20px; +} + +.tab > .active > a { + color:#222; + border-color: #1e87f0; +} + +.tab > li > a { + text-decoration: none; + cursor: pointer; +} + +.tab-content { + padding: 0; +} + +.tab-content > li { + display: none; +} +.tab-content > li.active { + display: block; +} + +/* The 2 following blocks can be removed if the script is not configured to show the toast message */ +#jekyll-tabs-copy-to-clipboard-message { + visibility: hidden; + min-width: 250px; + margin-left: -125px; + background-color: #333; + color: #fff; + text-align: center; + border-radius: 2px; + padding: 16px; + position: fixed; + z-index: 1; + right: 50%; + bottom: 30px; +} + +#jekyll-tabs-copy-to-clipboard-message.show { + visibility: visible; + -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s; + animation: fadein 0.5s, fadeout 0.5s 2.5s; +} \ No newline at end of file diff --git a/assets/js/tabs.js b/assets/js/tabs.js new file mode 100644 index 00000000..0c86ebe7 --- /dev/null +++ b/assets/js/tabs.js @@ -0,0 +1,5 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.jekyllTabs=t():e.jekyllTabs=t()}(self,(()=>(()=>{"use strict";var e={973:(e,t,o)=>{o.r(t),o.d(t,{addClass:()=>r,createElementFromHTML:()=>s,findElementsWithTextContent:()=>n,getChildPosition:()=>a});const a=e=>{const t=e.parentNode;for(let o=0;o{const o=document.querySelectorAll(e),a=[];for(let e=0;e{const t=document.createElement("template");return t.innerHTML=e.trim(),t.content.firstChild},r=(e,t,o)=>{e.className=e.className?`${e.className} ${t}`:t,setTimeout((()=>{e.className=e.className.replace(t,"").trim()}),o)}},39:(e,t,o)=>{o.r(t),o.d(t,{activateTabFromUrl:()=>d,addCopyToClipboardButtons:()=>u,appendToastMessageHTML:()=>b,copyToClipboard:()=>c,handleTabClicked:()=>i,removeActiveClasses:()=>l,syncTabsWithSameLabels:()=>y,updateUrlWithActiveTab:()=>p});const{getChildPosition:a,createElementFromHTML:n,findElementsWithTextContent:s,addClass:r}=o(973),l=e=>{const t=e.querySelectorAll("ul > li");Array.prototype.forEach.call(t,(e=>{e.classList.remove("active")}))},i=e=>{const t=e.parentNode,o=t.parentNode,n=a(t);if(t.className.includes("active"))return;const s=o.getAttribute("data-tab");if(!s)return;const r=document.getElementById(s);l(o),l(r),r.querySelectorAll("ul.tab-content > li")[n].classList.add("active"),t.classList.add("active")},c=(e,t)=>{if(navigator.clipboard&&window.isSecureContext)navigator.clipboard.writeText(e);else{const t=document.createElement("textarea");t.value=e,t.style.position="absolute",t.style.left="-999999px",document.body.prepend(t),t.select();try{document.execCommand("copy")}catch(e){console.error(e)}finally{t.remove()}}"function"==typeof t&&t()},d=()=>{var e;const t=null===(e=window.location.hash)||void 0===e?void 0:e.substring(1);if(!t)return;const o=document.getElementById(t);if(!o)return;const a=new URLSearchParams(window.location.search).get("active_tab");if(!a)return;const n=o.querySelector("li#"+a+" > a");n&&i(n)},p=e=>{const t=e.parentNode,o=t.parentNode,a=new URLSearchParams(window.location.search);a.set("active_tab",t.id);const n=window.location.pathname+"?"+a.toString()+"#"+o.id;history.replaceState(null,"",n)},u=({buttonHTML:e,showToastMessageOnCopy:t,toastDuration:o})=>{const a=document.querySelectorAll("ul.tab-content > li pre");for(let s=0;s{m(o)}),i.addEventListener("click",(()=>{c(r.innerText,d)}))}},b=e=>{const t=document.createElement("div");t.id="jekyll-tabs-copy-to-clipboard-message",t.textContent=e,document.getElementsByTagName("body")[0].appendChild(t)},m=e=>{r(document.getElementById("jekyll-tabs-copy-to-clipboard-message"),"show",e)},y=e=>{const t=s("a",e.textContent);for(let o=0;o{for(var a in t)o.o(t,a)&&!o.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var a={};return(()=>{o.r(a),o.d(a,{init:()=>i});const{activateTabFromUrl:e,updateUrlWithActiveTab:t,handleTabClicked:n,addCopyToClipboardButtons:s,syncTabsWithSameLabels:r,appendToastMessageHTML:l}=o(39),i=(o={})=>{const a={syncTabsWithSameLabels:!1,activateTabFromUrl:!1,addCopyToClipboardButtons:!1,copyToClipboardSettings:{buttonHTML:"",showToastMessageOnCopy:!1,toastMessage:"Code copied to clipboard",toastDuration:3e3}},i=Object.assign(Object.assign(Object.assign({},a),o),{copyToClipboardSettings:Object.assign(Object.assign({},a.copyToClipboardSettings),o.copyToClipboardSettings)}),c=document.querySelectorAll("ul.tab > li > a");if(Array.prototype.forEach.call(c,(e=>{e.addEventListener("click",(o=>{o.preventDefault(),n(e),i.activateTabFromUrl&&t(e),i.syncTabsWithSameLabels&&r(e)}),!1)})),i.addCopyToClipboardButtons){const e=i.copyToClipboardSettings;s(e),e.showToastMessageOnCopy&&l(e.toastMessage)}i.activateTabFromUrl&&e()}})(),a})())); + +window.addEventListener('load', function () { + jekyllTabs.init(); +}); \ No newline at end of file diff --git a/index.html b/index.html index 96c509c7..3fcf87ec 100755 --- a/index.html +++ b/index.html @@ -37,7 +37,7 @@

> -

CDO

+

CDO

Learn about CASE's parent organization.

diff --git a/ontology/get-started.md b/ontology/get-started.md new file mode 100644 index 00000000..e15dee1f --- /dev/null +++ b/ontology/get-started.md @@ -0,0 +1,267 @@ +--- +title: Get Started +jumbo_desc: Get started with CASE using code examples. +--- + +### Get Started with CASE + +The full collection of libraries, tools, and documentation for CASE is available on the [CASE GitHub](https://github.com/casework) repository. This guide walks through the anatomy of a CASE graph, full CASE example graph, and how to generate and query CASE graphs. + +### Anatomy of a CASE Graph + +CASE uses JSON-LD which is JSON with linked relationships between objects within the JSON. It contains two top-level keys, `@context` and `@graph`. The `@context` key is a mapping of aliases and namespaces that point to namespaces within the UCO and CASE ontologys. The `@graph` key is an array/list of objects that represent the data being exchanged. + +Each object within the `@graph` key is an Object, which contains at least an `@id` key that is a unique identifier for the object. The `@type` key is the aliased type of the object within the ontology. The available and required properties are defined in the ontology schema. A mimimal example is: + +```json +{ + "@id": "kb:location-4511219e-a924-4ba5-aee7-dfad5a2c9c05", + "@type": "uco-location:Location" +} +``` + + +### Full CASE Example Graph + +A full (basic) example of a CASE JSON-LD output is below: + +```json +{ + "@context": { + "@vocab": "http://example.org/local#", + "kb": "http://example.org/kb/", + "acme": "http://custompb.acme.org/core#", + "uco-core": "https://ontology.unifiedcyberontology.org/uco/core/", + "uco-location": "https://ontology.unifiedcyberontology.org/uco/location/", + "xsd": "http://www.w3.org/2001/XMLSchema#" + }, + "@graph": [ + { + "@id": "kb:location-4511219e-a924-4ba5-aee7-dfad5a2c9c05", + "@type": "uco-location:Location", + "uco-core:hasFacet": [ + { + "@id": "kb:simple-address-facet-59334948-00b9-4370-85b0-4dc8e07f5384", + "@type": "uco-location:SimpleAddressFacet", + "uco-location:locality": "Seattle", + "uco-location:region": "WA", + "uco-location:postalCode": "98052", + "uco-location:street": "20341 Whitworth Institute 405 N. Whitworth" + }, + { + "@id": "kb:acme-internal-location-facet-41fb3158-bbab-404d-97e4-ac61debb71f3", + "@type": [ + "acme:InternalLocationFacet", + "uco-core:Facet" + ], + "acme:floor": 3, + "acme:roomNumber": 345 + } + ] + }, + { + "@id": "kb:location-b579264d-6e30-4055-bf9b-72390364f224", + "@type": "uco-location:Location", + "uco-core:hasFacet": [ + { + "@id": "kb:simple-address-facet-258f169e-1e9c-4936-ba65-eed0f0c60788", + "@type": "uco-location:SimpleAddressFacet", + "uco-location:locality": "Paris", + "uco-location:country": "France", + "uco-location:postalCode": "F-75002", + "uco-location:street": "38 Bad Guy Headquarters st." + }, + { + "@id": "kb:lat-long-coordinates-facet-36126f9c-0273-48fe-ad4d-6a4e2848458f", + "@type": "uco-location:LatLongCoordinatesFacet", + "uco-location:latitude": { + "@type": "xsd:decimal", + "@value": "48.860346" + }, + "uco-location:longitude": { + "@type": "xsd:decimal", + "@value": "2.331199" + } + } + ] + } + ] +} +``` + +Additional examples are available in the [CASE-Examples](https://github.com/casework/CASE-Examples/tree/master/examples/illustrations) repository. This example was copied from [here](https://github.com/casework/CASE-Examples/blob/master/examples/illustrations/location/location.json). + + +### Basic Graph Creation + +{% tabs log %} + +{% tab log Python %} +```python +import json +import uuid + +# Build the base graph dictionary with the context. This must contain all the namespaces used in the graph +graph: dict = { + "@context": { + "kb": "http://example.org/kb/", + "uco-core": "https://ontology.unifiedcyberontology.org/uco/core/", + "uco-location": "https://ontology.unifiedcyberontology.org/uco/location/", + }, + "@graph": [] +} + +# Add an object to the graph +graph["@graph"].append({ + "@id": "kb:location-" + str(uuid.uuid4()), + "@type": "uco-location:Location", + "uco-core:hasFacet": [ + { + "@id": "kb:simple-address-facet-" + str(uuid.uuid4()), + "@type": "uco-location:SimpleAddressFacet", + "uco-location:locality": "Seattle", + "uco-location:region": "WA", + "uco-location:postalCode": "98052", + "uco-location:street": "20341 Whitworth Institute 405 N. Whitworth" + } + ] +}) + +# Write the dictionary to a JSON file +with open('case.jsonld', 'w') as f: + json.dump(graph, f, indent=4) +``` +{% endtab %} + +{% tab log C# %} +``` +{% endtab %} + +{% tab log C# %} +```cs +// TODO +``` +{% endtab %} + +{% tab log Java %} +```java +// TODO +``` +{% endtab %} + +{% endtabs %} + +### Validation Tools + +#### CLI +Once the JSON-LD output is generated, it can be validated for syntactic correctness using the [`case_validate`](https://github.com/casework/CASE-Utilities-Python) tool. The tool is available as a Python package and can be installed using `pip install case-utils`. + +```bash +pip install case-utils +``` + +Then the tool can be run on the generated JSON-LD file which will output any errors or warnings. +```bash +case_validate case.jsonld +``` + +#### Docker + +The `case_validate` tool is also available as a Docker image on [Docker Hub](https://hub.docker.com/r/kchason/case-validator). The container can be run with the following command: + +```shell +docker run --rm \ + -e CASE_PATH="/opt/case/" \ + -e CASE_VERSION="case-1.3.0" \ + -e CASE_EXTENSION_FILTER="jsonld" \ + -v "/path/to/local:/opt/case" \ + kchason/case-validator:latest +``` + +Full usage documentation is available at the `kchason/case-validator` [Docker Hub page](https://hub.docker.com/r/kchason/case-validator). + +#### GitHub Actions + +There is a GitHub Action available to validate CASE graphs in a CI/CD pipeline that are wrappers around the `case_validate` tool which makes it easier to test built graphs from unit and integrated testing frameworks. + +```yaml +- name: CASE Export Validation + uses: kchason/case-validation-action@v2.9.0 + with: + case-path: ./output/ + case-version: "case-1.3.0" + extension-filter: "jsonld" +``` + +Full usage documentation is available at the [GitHub Action Marketplace](https://github.com/marketplace/actions/case-ontology-validator) page for the CASE Ontology Validator. + +#### GitLab CI Component + +There is also GitLab CI component available to validate CASE graphs in a CI/CD pipeline that are wrappers around the `case_validate` tool which makes it easier to test built graphs from unit and integrated testing frameworks. + +```yaml +include: + - component: https://gitlab.com/keith.chason/case-validation-example/case-validate@0.1.0 + inputs: + case-path: tests/data +``` + +Full usage documentation is available at the [GitLab Repository](https://gitlab.com/keith.chason/case-validation-component). + +### Binding/Mapping Libraries + +Several libraries have been developed to assist in the generation of CASE graphs in various programming languages. These libraries provide a more programmatic way to generate CASE graphs using the language's native data structures. These have been categorized between "Bindings" and "Mappings". + +#### Bindings + +These libraries use RDF graphs to manage the CASE graph data. They provide a more direct mapping to the RDF data model and are more closely aligned with the underlying ontology but can be more resource intensive. + +| Language | Library | Repository | +|----------|----------|------------| +| C# | [CipherTech.CASE.Bindings](https://www.nuget.org/packages/CipherTech.CASE.Bindings) | [https://github.com/ciphertechsolutions/CASE-Bindings-CSharp](https://github.com/ciphertechsolutions/CASE-Bindings-CSharp) | + +#### Mappings + +These libraries use native data structures to manage the CASE graph data. They provide a more programmatic way to generate CASE graphs using the language's native data structures and are then serialized into JSON-LD. + +| Language | Library | Repository | +|----------|----------|------------| +| Python | [case-mapping](https://pypi.org/project/case-mapping/) | [https://github.com/casework/CASE-Mapping-Python](https://github.com/casework/CASE-Mapping-Python) | + + +### Full CASE Concepts + +The rendered documentation for the CASE Ontology, which contains the list of all classes and properties, is available at [https://ontology.caseontology.org](https://ontology.caseontology.org/documentation/index.html). + +All of the concepts used in CASE and UCO are identified with IRIs, which redirect to the concept's documentation when visited in a browser. See for example the location classes used above: + +* [`uco-location:Location`](https://ontology.unifiedcyberontology.org/uco/location/Location) +* [`uco-location:LatLongCoordinatesFacet`](https://ontology.unifiedcyberontology.org/uco/location/LatLongCoordinatesFacet) +* [`uco-location:SimpleAddressFacet`](https://ontology.unifiedcyberontology.org/uco/location/SimpleAddressFacet) + + +### Query CASE Graphs + +{% tabs log %} + +{% tab log Python %} +```python +# TODO +``` +{% endtab %} + +{% tab log C# %} +```cs +// TODO +``` +{% endtab %} + +{% tab log Java %} +```java +// TODO +``` +{% endtab %} + +{% endtabs %} + +A full set of examples for querying CASE graphs and converting them to a GeoJSON output format in several programming languages is available in the [CASE-Examples-Conversion](https://github.com/casework/CASE-Examples-Conversion) repository.