chore: move all entries to flat file

This commit is contained in:
thomasabishop 2024-02-16 15:19:05 +00:00
parent 2e69cb0d7c
commit 5e00c654b3
407 changed files with 18 additions and 705 deletions

View file

@ -1,35 +0,0 @@
---
categories:
- Databases
tags: [relational-databases]
---
# ACID principle
> A database is a collection of organised data that can be efficiently stored,
> sorted, and searched.
How the data is organised will often determine the _type_ of database used.
There are many different types of database; some examples of the different types
are relational, object-orientated, graphical, NoSQL, and distributed. All should
meet the principles of ACID.
To ensure the integrity of a database, each change or transaction must conform
to a set of rules known as ACID:
- **atomicity**
- when changing data within a database, if any part of the change fails, the
whole change will fail and the data will remain as it was before the change
was made; this is a safeguard that prevents partial records being created.
- **consistency**
- before data can be changed in a database, it must be validated against a set
of rules
- **isolation**
- databases allow multiple changes at the same time, but each change is
isolated from others
- **durability**
- once a change has been made, the data is safe, even in the event of system
failure
> Databases will have mechanisms for **backup**, **distribution**, and
> **redundancy**, to ensure data is not lost.

View file

@ -1,134 +0,0 @@
---
categories:
- DevOps
- Backend
tags: [AWS, docker]
---
# Local AWS development with SAM
We can run a local instance of our SAM stack for a given application without
sending requests to the cloud. This is implemented through Docker.
The SAM CLI handles all the Docker-related tasks, such as pulling the required
Lambda runtime images, creating containers, mounting your code and dependencies,
and running your Lambda functions inside those containers.
## Basic set up
Enter your project directory.
First build your SAM application:
```sh
sam build
```
We then run:
```sh
sam local start-api
```
If you have an API Gateway endpoint that you want to call over the local server.
You will be able to call it after executing the above.
This will be indicated by:
![](/_img/local-sam-docker.png)
If we want to invoke the function directly we use:
```sh
sam local invoke [FunctionName]
```
## Using environment variables
If you have an API key or database credentials, you are going to typically want
to use different values dependent on environment.
Even if the values are the same accross environments, it's a good idea to not
call a secret when working locally since this request is billable.
In the example below I show how to set up environment variables for an API key
locally and in production.
## Create secret
Go to AWS SecretsManager and add the API key as a secret. This will be sourced
in production.
## Create local ENV file
> These must be in JSON to work with SAM:
### Local env
```json
// local-env.json
{
"FunctionName": {
"API_KEY": "xxx-yyy-xxx",
"NODE_ENV": "development"
}
}
```
We save these to the root of the given function's directory not at the global
repo level.
> Be sure to add this to `.gitignore` so that it does not become public
### Update `template.yaml`
Every environment variable you intend to use, must exist in the `template.yaml`,
otherwise it will not be sourced at runtime:
```yaml
...
Resources:
Properties:
Environment:
Variables:
SECRET_ARN: "arn:aws:secretsmanager:eu-west-2:885135949562:secret:wakatime-api-key-X9oF3v",
NODE_ENV: production
API_KEY:
...
```
> We go ahead and populate the values for production. But we leave the variable
> we use in development blank, since we don't want it committed and we will
> source it at the SAM invocation. It still needs to exist though.
### Pass in the environment variable at invocation:
```sh
sam local start-api --env-vars /home/thomas/repos/lambdas/wakatime-api/get-coding-stats/local-env.json
```
In production, the variables required will be automatically sourced from the
`template.yaml`
### Create handler within the Lambda itself
You are obviously going to need to distinguish between the different deployments
when the Lambda executes. Here is an example helper function:
```ts
import * as AWS from "aws-sdk";
const secretsManager = new AWS.SecretsManager();
async function getApiKey(): Promise<string> {
if (process.env.NODE_ENV === "production") {
const response = await secretsManager
.getSecretValue({ SecretId: process.env.SECRET_ARN as string })
.promise();
const secretValues = JSON.parse(response.SecretString as string);
return secretValues.API_KEY;
} else {
return process.env.API_KEY as string;
}
}
```

View file

@ -1,299 +0,0 @@
---
categories:
- DevOps
- Backend
tags: [AWS]
---
# AWS SAM
SAM stands for **serverless application model**. It is a framework developed by
AWS to simplify the process of building, deploying and managing serverless
applications. It provides a concise syntax for defining the components of a
serverless application, such as
[Lambda functions](/DevOps/AWS/AWS_Lambda/Lambda_programming_model.md),
[API gateway](/DevOps/AWS/AWS_API_Gateway.md) and database tables.
The SAM infrastructure is defined in a YAML file which is then deployed to AWS.
SAM syntax gets transformed into CloudFormation during the deployment process.
(CloudFormation is a broader and more robust AWS tool for large, highly
scaleable infrastructures).
## Key features of SAM
- Single deployment configuration
- Integration with development tools
- Local testing and debugging
- Built on AWS CloudFormation
## Main technologies required
### Docker
Whilst SAM can be used to create a deployable file for AWS it can also be run as
a container for local development with Docker.
### AWS CLI
This is installed using Python and allows you to interact directly with AWS via
the command-line.
### AWS SAM CLI
See
[https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)
## Setting up credentials for the AWS CLI
You require an access key for the given
[IAM user](/DevOps/AWS/AWS_User_management_and_roles.md#iam). You should create
an IAM account specific to the project with bounded permissions.
```
aws configure
AWS Access Key ID [None]: AK*******
AWS Secret Access Key [None]: ukp******
Default region name [None]:
Default output format [None]:
```
This information can be found in the Security Credentials section of the given
[IAM](/DevOps/AWS/AWS_User_management_and_roles.md#iam) user:
![](/_img/access-key-aws.png)
### Switching between credentials
You should set up a different IAM user for each project.
You can do this with:
```sh
aws configure --profile <profile-name>
```
This will then ask you to add the credentials for the user.
You can switch between different credentials for the user as follows:
```sh
AWS_PROFILE=<profile-name> sam build
```
## Starting a SAM project
First create a directory for your project which will serve as the repository:
```sh
mkdir aws-sam-learning
cd aws-sam-learning
```
Then we can use the `sam` cli to bootstrap the project:
```sh
sam init --runtime nodejs16.x
```
We can just click through and accept the basic HelloWorld Lambda.
This will create the Lambda as well as an API Gateway trigger URL.
### `template.yaml`
This is autogenerated and details the main constituents of the project. There
are lots of fields but the most important are the following:
```yaml
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs16.x
Architectures:
- x86_64
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
```
This details the location of the
[handler function](/DevOps/AWS/AWS_Lambda/Lambda_handler_function.md) which is
contained at the path `hello-world/app.js`:
```js
exports.lambdaHandler = async (event, context) => {
try {
// const ret = await axios(url);
response = {
statusCode: 200,
body: JSON.stringify({
message: "hello world",
// location: ret.data.trim()
}),
};
} catch (err) {
console.log(err);
return err;
}
return response;
};
```
It also lists the `get` event that we can use to call API Gateway and trigger
the Lambda.
The full template is below:
![](/_img/sam-template-yaml.png)
## Adding our own code
We will create our own function and API Gateway trigger.
We will place our function after the existing `HelloWorldFunction`
```yaml
ClockFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: clock/
Handler: handler.clock
Runtime: nodejs16.x
Events:
ClockApi:
Type: Api
Properties:
Path: /clock
Method: get
```
We can test the syntax with:
```sh
sam validate
```
Just like with `HelloWorld`, we will create a directory for this function:
`clock` and we will initialise it as an `npm` project.
```sh
mkdir clock
cd clock
npm init
```
We will use `handler.js` as our root, handler function.
We have said in the template file that our `Handler: handler.clock`, therefore
the main function in the `handler` module should be `clock`:
```js
const moment = require("moment");
exports.clock = async (event) => {
console.log("Clock function run");
const message = moment().format();
const response = {
statusCode: 200,
body: JSON.stringify(message),
};
return response;
};
```
The directory structure is as follows:
![](/_img/sam-directory.png)
When we call the API Gateway path `/clock` with `GET`, our function will be
triggered.
## Deploying the project
We will now deploy our project to AWS from the local environment.
The process is as follows:
1. Build
2. Package
3. Deploy
### Build
We need to install the runtime dependencies for the function. We do this by
running `sam build`. This ignores test files and development dependencies and
installs the project dependencies and source files to a temporary subdirectory.
![](/_img/sam-build.png)
The build directory is `.aws-sam/build/`. There will be a subdirectory for each
of our files.
### Package
As noted, CloudFront handles the deployment of the application. It can only
receive one file as an input. The packaging process consists in creating that
single file.
The packaging proces will first archive all of the project artefacts into a zip
file and then upload that to [S3](/DevOps/AWS/AWS_S3.md). A reference to this S3
entity is then provided to CloudFormation.
![](/_img/s3-package-again.svg)
The command is as follows:
```sh
sam package
--template-file template.yaml
--output-template-file pkg.yml
--region eu-west-1
```
This will automatically create a hashed bucket name for you in S3 (I have tried
to add my own naming but it doesn't comply.)
### Local development with Docker
In order to work with your application locally without actually sending requests
to AWS and using credit, you can run a local instance.
See
[Local AWS Development with SAM](/DevOps/AWS/SAM/Local_AWS_development_with_SAM.md).
### Deploy
Once you have packaged the app you can deploy with `sam deploy --guided`. This
will talk you through the defaults and will deploy the package to
CloudFormation. In CloudFormation each individual project is called a **stack**.
If we then go to Cloud Formation we will see the deployed application.
![](/_img/cloud-formation-stack.png)
## Call the endpoint
If we now go to the Lambda console, we will see our function listed, and the API
Gateway endpoint under `triggers`:
![](/_img/gateway-trigger.png)
We can then call this from Postman to check everything is working as it should:
![](/_img/postman-aws-output.png)
## Clean up and erase the stack
We can delete the stack and remove all the resources we have created with a
single CLI method:
```sh
aws cloudformation delete-stack --stack-name <name> --region <region>
```

View file

View file

@ -1,156 +0,0 @@
---
categories:
- Programming Languages
tags:
- javascript
- react
- react-classes
---
# Managing forms with class components
We are going to create a simple form that takes two integers from input fields
and returns their sum. Here is the raw component:
```jsx
class Form extends React.Component {
render() {
return (
<form>
<input type="number" placeholder="Please enter an integer" required />
<input type="number" placeholder="Please enter an integer" required />
<button>Calculate sum</button>
<output></output>
</form>
);
}
}
```
## Adding handlers
```jsx
class Form extends React.Component {
handleSubmit = (event) => {
event.preventDefault();
// Specific state change on submit
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="number" placeholder="Please enter an integer" required />
<input type="number" placeholder="Please enter an integer" required />
<button>Calculate sum</button>
<output></output>
</form>
);
}
}
```
- The overall handling of the form is going to execute on the submission of the
form. So we create an `onSubmit` event within the form body and tie this to a
handling function (`handleSubmit`) on the class.
- We follow the custom of generic naming of event methods with `handle[event]` ,
just like we use `handleClick` with `onClick` events
- The `event` object is a React wrapper for the standard DOM event. We operate
on this object when managing the state of forms using React.
- `preventDefault` is just the React method of the traditional prevent default
method that we can apply to forms. It stops the page updating when the submit
button is clicked.
## Capturing input values through state change
In order to retrieve the values that the user enters we need to introduce state
to the form component. This is managed using the `onChange` event. Even though
the overall form action should be managed through `onSubmit` we need to
initialize state on the inputs we intend to use when the form is submitted. This
enshrines the React principle of **lifting state up**. As the `input` elements
are constituents of the overall `form` component, their state should be handled
by this common parent.
### Initializing `input` state with `onChange` events
To begin with let's just look at how we would do this in the abstract with a
single `input` before applying it to the two inputs in our example:
```html
<input
type="number"
placeholder="Please enter an integer"
value="this.state.integerFirst"
onChange="this.handleChange"
required
></input>
```
We tie the state parameters in the `input` element to a `handleChange` function
on the parent component but we first need to add the starting state (using
`this.state` on the `Form` components' constructor:
```jsx
class Form extends React.Component {
constuctor(props) {
super(props);
this.state = {
integerFirst: " "
};
}
handleChange = (event) => {
this.setState(
{
integerFirst: event.target.value
}
}
```
Now the `Form` component is kept in the loop. Whenever the user alters the
`input` field, this change will be logged as a state change within React. This
will allow us to retrieve the values that the inputs have when the submit event
fires.
## Handling multiple inputs
It would be inefficient to have a change handler for every input in a form with
many inputs. In this scenario it is better to use destructuring to capture all
the values:
```jsx
class Form extends React.Component {
constuctor(props) {
super(props);
this.state = {
integerFirst: "",
integerSecond: ""
};
}
returnSum = (x,y) => x + y;
handleSubmit = (event) => {
event.preventDefault()
this.setState({
outputSum: this.returnSum(
Number(this.state.integerFirst), Number(this.state.integerSecond)
)
});
handleChange = (event) => {
this.setState(
{
[event.targe.name]: value
}
)}
render() {
return(
<form onSubmit={this.handleSubmit}>
<input type="number" name="integerFirst" placeholder="Please enter an integer" onChange="this.handleChange" required />
<input type="number" name="integerSecond" placeholder="Please enter an integer" onChange="this.handleChange" required />
<button>Calculate sum</button>
<output></output>
</form>
)
}};
```

View file

@ -1,77 +0,0 @@
---
categories:
- Programming Languages
tags:
- typescript
---
# Functions
## Basic typing within a function: arguments and return values
With functions we can apply types to the return value, the parameters and any
values that are included within the function body.
```ts
function search(query: string, tags: string[]): string {}
```
We can also specify optional parameters with use of the `?` symbol:
```ts
function search(query: string, tags?: string[]): string {}
```
### Utilising custom types
Whilst we can use standard JS types with the parameters and return value, the
real benefit comes when you use custom types. For instance we can specify that
an object passed to a function must match the shape of a custom type or
interface. Similarly we can ensure that for functions that return objects, the
object that is returned must satisfy the shape of the custom object.
```ts
async function getContributorData(
contributorName: string
): Promise<IContributor> {}
```
For example, this function has a return signature which indicates that it will
return a promise matching a type of shape `IContributor`
## Functions as types
As well as typing the values that a function receives and returns, you can type
the function itself. **This is most useful when you are using higher-order
functions and passing functions as parameters to another function.** In these
scenarios you will want to type the function that is being passed as a
parameter. There are several ways to do this. We'll use the following basic
function as our demonstration:
```ts
function higherOrderFunction(integer: number, addFunction: any): number {
return addFunction(integer);
}
```
### Use `typeof`
```ts
// Declare an adding function
const addTwo = (int: number) => int + 2;
// Apply it:
higherOrderFunction(3, addTwo);
// We can now define the higher-order function with a specific type:
function higherOrderFunction(
integer: number,
addFunction: typeof addTwo
): number {
return addFunction(integer);
}
```
This way we just use the native `typeof` keyword to assert that any call of
`hoFunc` should pass a function of the type `addTwo`

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

15
_scripts/flatten_directories.sh Executable file
View file

@ -0,0 +1,15 @@
#!/bin/bash
directories_to_parse="../Computer_Architecture ../Databases ../Electronics_and_Hardware ../Operating_Systems ../Programming_Languages ../DevOps"
directory="/home/thomas/repos/eolas-bak"
mapfile -t directories < <(find "$directory" -type d)
file_matches=()
# Return array of all files belonging to source dirs...
for ele in ${directories[@]}; do
file_matches+=( $(find $ele -name "*.md" -type f) )
done
echo ${file_matches[1]}

View file

@ -6,3 +6,6 @@ find /home/thomas/repos/eolas/ -type f -name "*.md" | while
read file; do read file; do
sed -i 's/\/img\//\/_img\//g' $file sed -i 's/\/img\//\/_img\//g' $file
done done