...
 
Commits (2)
......@@ -5,7 +5,7 @@ title: Architecture
## Overview
> TODO update this diagram
> TODO: update this diagram
![Architecture diagram](assets/architecture-diagram.jpg 'Diagram of Minds architecture')
[Click to enlarge](assets/architecture-diagram.jpg)
......
This diff is collapsed.
......@@ -9,13 +9,13 @@ Minds deploys with [GitLab](https://gitlab.com/minds), making use of Docker & Ku
### FPM
```
```console
docker build -t minds/fpm:latest -f containers/php-fpm/Dockerfile .
```
### Runners
```
```console
docker build -t minds/runners:latest -f containers/php-runners/Dockerfile .
```
......@@ -50,7 +50,7 @@ The review apps make use of [Helm](https://helm.sh) and [Kubernetes](https://kub
A kubernetes environment can be created by running:
```
```console
helm upgrade \
--install \
--reuse-values \
......@@ -84,7 +84,7 @@ To have your values persist across builds, you must extend the settings.php scri
Do not hard code the values in the configMap, reference them via `.Values.key.subkey`:
```
```console
// Twillio configuration
$CONFIG->set('twilio', [
'account_sid' => '{{ .Values.twilio.sid }}',
......
......@@ -29,7 +29,7 @@ The docs are the SSOT for information about how to configure, use, and troublesh
- If the answer to a question exists in documentation, share the link to the docs instead of rephrasing the information.
- When you encounter new information not available in GitLab’s documentation (for example, when working on a support case or testing a feature), your first step should be to create a merge request to add this information to the docs. You can then share the MR in order to communicate this information.
- When you encounter new information not available in Minds’s documentation (for example, when working on a support case or testing a feature), your first step should be to create a merge request to add this information to the docs. You can then share or screenshot the MR in order to communicate this information.
### Link instead of summarize
......@@ -39,7 +39,7 @@ There is a temptation to summarize the information on another page. This will ca
Use [GitHub flavored markdown](https://help.github.com/en/articles/basic-writing-and-formatting-syntax).
#### Headers
### Headers
The largest tier of headers should have _two_ hashes, e.g. `## My h1 primary header`. If you use one hash, it won't work with the navigation sidebar.
......@@ -48,19 +48,9 @@ Only capitalize the _first letter_ of your header, unless it includes a proper n
- `### My cool subheader with many words`
- `### My cool Minds subheader that contains a proper noun`
## Adding images
- Add the image file to `docs/assets/`
- Add a "Click to enlarge" link below images that depict small details/text
```md
![My cool diagram](assets/my-cool-diagram.png "My cool diagram's alt text")
[Click to enlarge](assets/my-cool-diagram.png)
```
## Editing an existing docs page
Edit by clicking the "edit" button at the top of the docs site page, or by navigating to `docs/` and editing the corresponding document:
Edit by clicking the "edit" button at the top of the docs site page, or by navigating to `docs/` (or `website/blog`, if it's a blog) and editing the corresponding document:
`docs/doc-to-be-edited.md`
......@@ -73,24 +63,7 @@ title: This Doc Needs To Be Edited
Edit me...
```
For more information about docs, click [here](https://docusaurus.io/docs/en/navigation)
## Editing an existing blog post
Edit blog posts by clicking the "edit" button at the top of the docs site page, or by navigating to `website/blog` and editing the corresponding post:
`website/blog/post-to-be-edited.md`
```markdown
---
id: post-needs-edit
title: This Blog Post Needs To Be Edited
---
Edit me...
```
For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog)
Click [here](https://docusaurus.io/docs/en/navigation) for more info on docs, or [here](https://docusaurus.io/docs/en/adding-blog) for blogs.
## Adding Content
......@@ -113,7 +86,7 @@ My new content here..
// Add newly-created-doc to the Getting Started category of docs
{
"docs": {
"Getting Started": [
"Getting started": [
"quick-start",
"newly-created-doc" // new doc here
],
......@@ -123,7 +96,9 @@ My new content here..
}
```
For more information about adding new docs, click [here](https://docusaurus.io/docs/en/navigation)
Re-run `yarn start` to see the changes in the sidebar.
For more information about adding new docs, click [here](https://docusaurus.io/docs/en/navigation).
### Adding a new blog post
......@@ -171,18 +146,18 @@ For more information about blog posts, click [here](https://docusaurus.io/docs/e
/* you can add custom pages */
{ page: 'help', label: 'Help' },
/* you can add external links */
{ href: 'https://github.com/facebook/Docusaurus', label: 'GitHub' },
{ href: 'https://gitlab.com/minds', label: 'GitLab' },
...
],
...
}
```
For more information about the navigation bar, click [here](https://docusaurus.io/docs/en/navigation)
For more information about the navigation bar, click [here](https://docusaurus.io/docs/en/navigation).
### Adding custom pages
1. Docusaurus uses React components to build pages. The components are saved as .js files in `website/pages/en`:
1. Docusaurus uses React components to build pages. The components are saved as .js files in `website/pages/en`
1. If you want your page to show up in your navigation header, you will need to update `website/siteConfig.js` to add to the `headerLinks` element:
`website/siteConfig.js`
......@@ -200,10 +175,18 @@ For more information about the navigation bar, click [here](https://docusaurus.i
For more information about custom pages, click [here](https://docusaurus.io/docs/en/custom-pages).
## Full docusaurus documentation
### Adding an image to a docs page
Full documentation can be found on the [docusaurus website](https://docusaurus.io/).
- Add the image file to `docs/assets/`
- Add a "Click to enlarge" link below images that depict small details/text
```md
![My cool diagram](assets/my-cool-diagram.png "My cool diagram's alt text")
[Click to enlarge](assets/my-cool-diagram.png)
```
## Credits
## Resources and credits
Full documentation can be found on the [docusaurus website](https://docusaurus.io/).
A portion of this guide's content is taken from GitLab's excellent [documentation styleguide](https://git.causal.ch/help/development/documentation/styleguide.md).
......@@ -42,11 +42,11 @@ front
### Common
In most cases, new code will be stored inside subject-specific module folders. However, if you are making something that will be used throughout the site, put it in `/common/` so it can be easily accessed from other modules. Some examples of the kind of things that belong in `/common/`:
In most cases, new code will be stored inside subject-specific module folders. However, if you are making something that will be used throughout the site, put it in the `common/` folder so it can be easily accessed from other modules. Some examples of the kind of things that belong in `common/`:
- **Directives**: user profile hovercard, tooltips, things related to Material design lite (slider, switch, date time picker), etc.
- **Pipes**: ... examples ...
- What else is in here...
- **Pipes**: TODO
- **Services**: TODO
## Naming conventions
......@@ -95,23 +95,9 @@ prettier --write "src/**/*.{scss,ts,html}"
## Spec tests
### Executing
We test our code to prevent software defects and verify that it behaves as we expect it to.
`ng test`
### Cypress tests
> TODO: Brian
#### Select with data attributes, not classes
This is Cypress' best practice for finding testable attributes in the DOM, since using HTML selectors to find positions in the DOM is both brittle and flakey. So wherever you see `data-name-of-component`, you'll know it's e2e related.
For example, to add data attributes to our `minds-activity` objects:
```html
[attr.data-minds-activity-guid]="activity.guid"
```
> See the [frontend tests walk-through](../walk-throughs/frontend-tests) for information on writing and running tests
## Styles
......@@ -141,4 +127,4 @@ _All_ colors should be defined using the `m-theme` mixin:
}
```
If something is black or white and you want to _not_ change it when the theme is changed (e.g. an overlay modal background should always be black, regardless of theme), use `$m-black-always` or `$m-white-always`.
If something is black or white and you want it to _not_ change when the theme is changed (e.g. you want an overlay modal background to always be black, regardless of theme), use `$m-black-always` or `$m-white-always`.
......@@ -83,7 +83,7 @@ Labels should be applied to issues at various stages of the issues workflow:
- [Squad::](https://gitlab.com/groups/minds/-/labels?utf8=✓&subscribed=&search=squad::) ~Blue, ~Green, ~Yellow
**_Once it's squad decides which sprint to assign it to_**
**_Once its squad decides which sprint to assign it to_**
- [Sprint::](https://gitlab.com/groups/minds/-/labels?utf8=✓&subscribed=&search=sprint::) ~10/09 - Pink Panther, ~12/30 - Understood Unicorn, etc.
......@@ -135,11 +135,11 @@ _Closes issue #7049."_
### Using the staging environment
Once the pipeline has passed for your MR, you can test it in the staging environment by clicking the "view app" button on the MR page. If there isn't already a "view app" button, click the 4th icon on the pipeline (the row of primarily green checkmarks - the 4th one should be titled "review:"). From the dropdown that appears, select "review:start".
Once the pipeline has passed for your MR, you can test it in the staging environment by clicking the "view app" button on the MR page. If there isn't already a "view app" button, click the 4th icon on the pipeline (the row of primarily green checkmarks - the 4th one should be titled "review:"). From the dropdown that appears, select "review:start". Wait for the app to build (it may take ~20-30 mins) before you can start using it.
### QA
When an MR is ready to be tested, add a "QA" approval rule and assign at least one approver to conduct testing. Include some testing guidelines in your MR description to point your tester in the right direction.
When an MR is ready to be tested by someone else, add a "QA" approval rule and assign at least one approver to conduct testing. Include some testing guidelines in your MR description to point your tester in the right direction.
## Bug lifecycle
......
---
id: backend-modules
title: Backend modules
---
To illustrate how modules work, we're going to create a VideoChat module that makes use of dependency injection providers.
Our completed module will look something like this:
![Module diagram](assets/engine-module-diagram.png 'Diagram of an example VideoChat module')
[Click to enlarge](assets/engine-module-diagram.png)
## Module building blocks
### The Model
`Core/VideoChat/VideoChat.php`
- The characteristics that define a single unit of whatever content your module is going to handle (e.g. a single video chat might include an array of _participantGuids, startTimestamp, endTimestamp_, etc.
- You'll want to `use Minds\Traits\MagicAttributes` so you can automatically use `get`, `set`, and `is` methods on each of your model's properties
- If you'll be using an api endpoint to interact with the frontend, include a `public function export() {...}` here so it can be picked up by your controller's `Factory::exportable()` function, which transforms arrays into an exportable format
- May include functions for calculated fields, e.g. `getUrn()`
### The Manager
`Core/VideoChat/Manager.php`
- Interfaces with the repository
- Hydrate the entities returned in the response here (if needed)
- Functions might include `get()`, `getList()`, `add()`, `update()`, `delete()`
- A given function might query the repository, hydrate entity guids that are returned in the response, and call delegates
```php
<?php
namespace Minds\Core\VideoChat;
class Manager
{
/** @var Repository $repository */
private $repository;
/** @var NotificationDelegate $notificationDelegate */
private $notificationDelegate;
public function __construct($repository = null, $notificationDelegate = null)
{
$this->repository = $repository ?: Di::_('Repository');
$this->notificationDelegate = $notificationDelegate ?: new Delegates/NotificationDelegate;
}
/**
* Add a model
* @param Model $model
* @return void
*/
public function add(Model $model)
{
$this->repository->add($model);
$this->notificationDelegate->onAdd($model);
}
```
_Note how we are able to switch out repositories here without impacting the wider codebase by interacting with our Manager. During migrations, a double writing pattern could be added with a few lines._
### The Repository
`Core/VideoChat/Repository.php`
- Interfaces with the database.
- Should **only ever** be referenced by its _Manager_. This single point of contact is essential because it makes it easy to replace legacy databases with new ones that are more suitable for our needs
- Should `use Minds\Common\Repository\Response`
- Functions might include `get()`, `getList()`, `add()`, `update()`, `delete()`
```php
<?php
class Repository
{
/** @var Client $db */
private $db
public function __construct($db = null)
{
$this->db = $db ?: Di::_()->get('Database\Cassandra\Cql');
}
public function add(Model $model)
{
...
}
...
```
### The Provider
`Core/VideoChat/Provider.php`
Defines a function that registers your _Manager_ to make it available for dependency injection.
```php
<?php
namespace Minds\Core\VideoChat;
use Minds\Core\Di\Provider as DiProvider;
class Provider extends DiProvider
{
public function register()
{
$this->di->bind('VideoChat\Manager', function ($di) {
return new Manager();
}, ['useFactory' => true]);
}
}
```
### The Module
`Core/VideoChat/Module.php`
Creates an instance of your _Provider_ and calls its `register()` function.
```php
<?php
namespace Minds\Core\VideoChat;
use Minds\Interfaces\ModuleInterface;
class Module implements ModuleInterface
{
public function onInit()
{
$provider = new Provider();
$provider->register();
}
}
```
Modules must be registered in [Core/Minds.php](https://gitlab.com/minds/engine/blob/master/Core/Minds.php):
```php
private $modules = [
...
VideoChat\Module::class,
];
```
### Delegates
`Core/VideoChat/Delegates/NotificationDelegate.php`
- Used to keep the _Manager_ clean
- Should be used to execute small, stateless outbound functions that don't return values that need further processing. (If this doesn't happen in your module, you don't need to use delegates)
- You can use any name you wish, we are using _NotificationDelegate_ as an example
```php
<?php
class NotificationDelegate
{
public function onAdd(Model $model)
{
...
}
...
```
### Tests
_Managers_, _Repositories_ and _Delegates_ should have 100% [spec test](./backend-tests) coverage.
Make sure you include `@package Minds\Core\VideoChat` in `VideoChat.php` so it can be picked up by [phpspec](https://www.phpspec.net/en/stable/) in `VideoChatSpec.php`.
---
id: backend-tests
title: Backend tests
---
This guide covers how to create and run [phpspec](https://www.phpspec.net/en/stable/) spec tests in the backend.
Specs are highly abstracted alter-egos of your _Manager_, _Repository_ and _Delegate_ files (see the [backend modules walk-through](./backend-modules) for more about those files) that allow you to focus on the bare-bones concepts of what those files will eventually do when they reach their final form (so you can plan effectively before tackling practical technicalities and specifics).
## Create a new test
To create a new test:
```console
bin/phpspec run Minds/Your/Namespace/ClassYouWantToSpec
```
This will create a folder in `minds/engine/` with a skeleton `ClassYouWantToSpecSpec.php` file and automatically import the default classes.
### Mock everything
_If you don't own the object, mock it._
There's a lot going on under the hood in the modules, and some of them have low level connectivity to the data stores.
Thankfully, phpspec Prophecy makes it really easy to mock - just be sure to import the class you want to mock inside your unit test.
#### Mock classes
Phpspec's `let()` function gets run before each test. If you provide a typed parameter, it will be mocked to the matching namespace.
```php
use PhpSpec\ObjectBehavior;
use Minds\Entities\Entity;
class ManagerSpec extends ObjectBehavior {
protected $entity;
let(Entity $entity) {
//Store a reference to the mock for reuse.
$this->entity = $entity
}
}
```
#### Mock functions
Phpspec will provide mocks via function parameters in your test:
```php
use PhpSpec\ObjectBehavior;
use Minds\Entities\Entity;
class ManagerSpec extends ObjectBehavior {
public function it_should_run_test(Entity $entity) {
}
}
```
### Set expectations
These mockable objects can then configure to simulate what will happen with special functions available on the mock.
`ShouldBeCalled()` will set the expectation that mocked method should be called with a parameter:
```php
$entity->setGuid(123)->shouldBeCalled();
```
`willReturn()` simulates the response of a mocked method:
```php
$entity->setGuid(123)->willReturn($entity);
```
### Access the underlying object
Sometimes, php will choke on the reflection of these mocked objects (especially when constructing other objects).
To instantiate an object and still be mockable:
```php
$mockedObject->getList()->willReturn([]);
$service = new ServiceThatNeedsDependencies($mockedObject->getWrappedObject());
```
## Running tests
To execute all tests:
```console
bin/phpspec run
```
To run a specific spec file (or folder), add its name:
```console
bin/phpspec run Spec/Core/VideoChats/Repository.php
```
To run a specific test inside of that spec file, add its starting line number:
```console
bin/phpspec run Spec/Core/VideoChats/Repository.php:42
```
### Verbose output
Running this command will give you deep output of the tests:
```console
bin\phpspec run -vvv
```
......@@ -3,6 +3,8 @@ id: emails
title: Sending emails
---
This guide covers how to dynamically send emails in the Minds backend.
## Process overview
1. An event occurs (e.g. a new user joins Minds) and dispatches a message to the queue
......
......@@ -3,10 +3,14 @@ id: feature-flags
title: Feature flags
---
Feature flags allow new features to be introduced to a subset of users (i.e. those in Canary mode) before they are available to all users. Start by enabling a feature flag and then wrap a gate around code that you want to be executed for applicable users.
Feature flags allow new features to be introduced to a subset of users (i.e. those in Canary mode) before they are available to all users.
## Usage
Start by configuring a feature flag and then wrap a "gate" around code that you want to be executed only for applicable users.
### Configure settings
First, define and enable the flag in `settings.php`:
```php
......@@ -17,7 +21,7 @@ $CONFIG->set('features', [
])
```
In the backend:
### Backend
```php
use Minds\Core\Features\Manager as FeaturesManager;
......@@ -34,7 +38,9 @@ if ($this->features->has('my-cool-feature')) {
}
```
It works similarly in the frontend. Import `FeaturesService`, add it to the constructor, and:
### Frontend
Import `FeaturesService`, add it to the constructor, and:
```ts
if (this.featuresService.has('my-cool-feature')) {
......
---
id: frontend-tests
title: Frontend tests
---
This guide covers how to create and run spec tests in the frontend.
## Testing frameworks
Minds uses [Cypress](https://www.cypress.io/) for e2e testing.
We also have legacy tests that use [Jasmine](https://jasmine.github.io) and [Karma](https://karma-runner.github.io/latest/index.html).
## Create a new test
> TODO: Brian
### Select with data attributes, not classes
This is Cypress' best practice for finding testable attributes in the DOM, since using HTML selectors to find positions in the DOM is both brittle and flakey. So wherever you see `data-name-of-component`, you'll know it's e2e related.
For example, to add data attributes to our `minds-activity` objects:
```html
[attr.data-minds-activity-guid]="activity.guid"
```
### Running tests
To run Cypress tests:
> TODO
To run Jasmine/Karma tests:
```console
ng test
```
......@@ -3,6 +3,8 @@ id: notifications
title: Notifications
---
This guide covers how to dynamically send notifications to users when an event occurs.
## Backend events and settings
Let's say we want to create a notification to send to video chat participants after a chat has completed.
......@@ -36,8 +38,6 @@ Also add your `notification_view` to:
- [Notification/Manager.php](https://gitlab.com/minds/engine/blob/master/Core/Notification/Manager.php) to determine which notification category it belongs to (e.g boosts, subscriptions, votes, etc.)
- [Notification/Extensions/Push.php](https://gitlab.com/minds/engine/blob/master/Core/Notification/Extensions/Push.php)
> TODO: What is Push.php? and should referrals be in there?
## Frontend template
Set up the template in [notification.component.html](https://gitlab.com/minds/front/blob/master/src/app/modules/notifications/notification.component.html):
......
......@@ -43,9 +43,9 @@ class Footer extends React.Component {
this.props.language
)}
>
Getting Started
Getting started
</a>
<a href={this.docUrl('guides/deployment', this.props.language)}>
<a href={this.docUrl('guides/architecture', this.props.language)}>
Guides
</a>
<a
......
......@@ -39,12 +39,21 @@
"guides/mobile": {
"title": "Mobile"
},
"walk-throughs/backend-modules": {
"title": "Backend modules"
},
"walk-throughs/backend-tests": {
"title": "Backend tests"
},
"walk-throughs/emails": {
"title": "Sending emails"
},
"walk-throughs/feature-flags": {
"title": "Feature flags"
},
"walk-throughs/frontend-tests": {
"title": "Frontend tests"
},
"walk-throughs/infinite-scroll": {
"title": "Infinite scroll"
},
......@@ -58,7 +67,7 @@
"Minds.com": "Minds.com"
},
"categories": {
"Getting Started": "Getting Started",
"Getting started": "Getting started",
"Guides": "Guides",
"Contributing": "Contributing",
"Walk-throughs": "Walk-throughs"
......
{
"docs": {
"Getting Started": [
"Getting started": [
"getting-started/introduction",
"getting-started/installation"
],
......@@ -15,6 +15,9 @@
],
"Contributing": ["contributing/contributing", "contributing/license"],
"Walk-throughs": [
"walk-throughs/backend-modules",
"walk-throughs/backend-tests",
"walk-throughs/frontend-tests",
"walk-throughs/emails",
"walk-throughs/feature-flags",
"walk-throughs/infinite-scroll",
......
......@@ -60,18 +60,14 @@ const siteConfig = {
},
/* Custom fonts for website */
/*
fonts: {
myFont: [
"Times New Roman",
"Serif"
],
myOtherFont: [
"-apple-system",
"system-ui"
]
},
*/
// fonts: {
// myFont: ['Roboto', 'Sans-Serif']
// myOtherFont: [
// "-apple-system",
// "system-ui"
// ]
// },
// This copyright info is used in /core/Footer.js and blog RSS/Atom feeds.
copyright: `Copyright © ${new Date().getFullYear()} Minds Inc.`,
......@@ -83,7 +79,6 @@ const siteConfig = {
// Add custom scripts here that would be placed in <script> tags.
scripts: [
'https://buttons.github.io/buttons.js',
'https://buttons.github.io/buttons.js',
'https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js',
'/js/code-block-buttons.js'
......@@ -108,7 +103,12 @@ const siteConfig = {
// template. For example, if you need your repo's URL...
// repoUrl: 'https://github.com/facebook/test-site',
customDocsPath: './docs'
customDocsPath: './docs',
algolia: {
apiKey: '6e560f4f51933177f40205eac88af5a4',
indexName: 'minds',
algoliaOptions: {} // Optional, if provided by Algolia
}
};
module.exports = siteConfig;
/* your custom css */
.homeContainer {
background-color: #021f3d;
background-color: #20232a;
background-image: url(../img/canyon-hero.jpg);
background-size: cover;
}
......@@ -52,6 +52,10 @@ a:hover {
color: #4690df;
}
.navBreadcrumb h2 i {
padding: 0 9px 0 3px;
}
.fixedHeaderContainer {
border-bottom: 1px solid #e8e8e8;
}
......@@ -101,6 +105,14 @@ a:hover {
.docMainWrapper .wrapper {
padding-top: 24px;
}
.navigationWrapper .algolia-docsearch-footer {
padding-right: 6px;
}
}
@media only screen and (max-width: 1024px) {
.reactNavSearchWrapper .algolia-autocomplete input#search_input_react {
padding-top: 2px;
}
}
@media only screen and (min-width: 1400px) {
......@@ -108,3 +120,63 @@ a:hover {
@media only screen and (min-width: 1500px) {
}
/* SEARCH ***********************************************/
.navigationWrapper .algolia-autocomplete {
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 20px;
}
.reactNavSearchWrapper .algolia-autocomplete input#search_input_react {
background-color: white !important;
color: #666 !important;
}
.navSearchWrapper:before {
border: 2px solid rgba(0, 0, 0, 0.47) !important;
width: 7px;
height: 7px;
}
.navSearchWrapper:after {
background: rgba(0, 0, 0, 0.47) !important;
width: 2px;
top: 56%;
}
.navigationWrapper .algolia-autocomplete .aa-dropdown-menu {
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 8px 0 rgba(0, 0, 0, 0.12);
border-radius: 4px;
border: 1px solid #e8e8e8;
}
.navigationWrapper .algolia-autocomplete .algolia-docsearch-suggestion--category-header {
border: 1px solid #606060;
color: #fff;
background: #353942;
}
.navigationWrapper .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column {
background: #f1f1f1;
}
.navigationWrapper .algolia-autocomplete .algolia-docsearch-suggestion--title {
}
.navigationWrapper .algolia-autocomplete .algolia-docsearch-suggestion--text {
color: #555;
}
.navigationWrapper .algolia-autocomplete .algolia-docsearch-suggestion--highlight {
color: #4690df !important;
}
.navigationWrapper .algolia-docsearch-footer {
border-top-color: #888;
color: #666;
padding-top: 8px;
font-size: 10px;
}
.navigationWrapper .algolia-docsearch-footer a {
font-size: 10px;
}