Skip to content

Commit

Permalink
Created new unlocked package (#6)
Browse files Browse the repository at this point in the history
* Converted repo to be an sfdx project, bumped metadata to API v56.0

* Created new unlocked package, with an initial package version v2.1.0
  • Loading branch information
jongpie authored Jan 11, 2023
1 parent 366fe56 commit 82077d6
Show file tree
Hide file tree
Showing 21 changed files with 6,760 additions and 259 deletions.
16 changes: 16 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
**/lwc/**/*.css
**/lwc/**/*.html
**/lwc/**/*.json
**/lwc/**/*.svg
**/lwc/**/*.xml
**/aura/**/*.auradoc
**/aura/**/*.cmp
**/aura/**/*.css
**/aura/**/*.design
**/aura/**/*.evt
**/aura/**/*.json
**/aura/**/*.svg
**/aura/**/*.tokens
**/aura/**/*.xml
**/aura/**/*.app
.sfdx
12 changes: 12 additions & 0 deletions .forceignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status
# More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm
#

package.xml

# LWC configuration files
**/jsconfig.json
**/.eslintrc.json

# LWC Jest
**/__tests__/**
47 changes: 38 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
#Folders to exclude
.settings/
config/
debug/
deploy/
# This file is used for Git repositories to specify intentionally untracked files that Git should ignore.
# If you are not using git, you can delete this file. For more information see: https://git-scm.com/docs/gitignore
# For useful gitignore templates see: https://github.com/github/gitignore

#Files to exclude
# Salesforce cache
.sf/
.sfdx/
.localdevserver/
.vscode/
deploy-options.json

# LWC VSCode autocomplete
**/lwc/jsconfig.json

# Code coverage reports
test-coverage/

# Logs
logs
*.log
*.sublime-project
*.sublime-workspace
*.sublime-settings
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Dependency directories
node_modules/

# Eslint cache
.eslintcache

# MacOS system files
.DS_Store

# Windows system files
Thumbs.db
ehthumbs.db
[Dd]esktop.ini
$RECYCLE.BIN/

# Local environment variables
.env
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm run precommit
10 changes: 10 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# List files or directories below to ignore them when running prettier
# More information: https://prettier.io/docs/en/ignore.html
#

**/staticresources/**
.localdevserver
.sfdx
.vscode

coverage/
13 changes: 13 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"trailingComma": "none",
"overrides": [
{
"files": "**/lwc/**/*.html",
"options": { "parser": "lwc" }
},
{
"files": "*.{cmp,page,component}",
"options": { "parser": "html" }
}
]
}
7 changes: 0 additions & 7 deletions .travis.yml

This file was deleted.

58 changes: 34 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
# Apex UUID
[![Build Status](https://travis-ci.org/jongpie/ApexUuid.svg?branch=master)](https://travis-ci.org/jongpie/ApexUuid)

<a href="https://githubsfdeploy.herokuapp.com" target="_blank">
<img alt="Deploy to Salesforce" src="https://raw.githubusercontent.com/afawcett/githubsfdeploy/master/deploy.png">
</a>
Provides a way to generate a [UUID (Universally Unique Identifier)](https://en.wikipedia.org/wiki/Universally_unique_identifier) in Salesforce's Apex language. This uses Verion 4 of the UUID standard - more details available [here](<https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)>)

## Usage
Provides a way to generate a [UUID (Universally Unique Identifier)](https://en.wikipedia.org/wiki/Universally_unique_identifier) in Salesforce's Apex language. This uses Verion 4 of the UUID standard - more details available [here](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random))
## Unlocked Package (no namespace) - v2.1.0

[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t4x000000NYNEAA4)
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t4x000000NYNEAA4)

# Getting Started

## Generating & Using A UUID

### Generating & using a UUID
To generate a UUID, simply instantiate a new instance. The string value can be retrieved with `getValue()`
```

```java
Uuid myUuid = new Uuid();
String myUuidValue = myUuid.getValue();
```
Expand All @@ -19,12 +22,12 @@ You can use the UUID value as a unique ID, generated by Apex. This lets you do s

It's best if you create a custom field on your object to store the UUID value with these properties: Text(36) (External ID) (Unique Case Insensitive).

In the code samples below, the field name Uuid__c is used as an example field name.
In the code samples below, the field name Uuid\_\_c is used as an example field name.

> ##### Example: Using a code-generated external ID (such as a UUID), we can create multiple accounts and related contacts with only 1 DML statement
> #### Example: Using a code-generated external ID (such as a UUID), we can create multiple accounts and related contacts with only 1 DML statement
```
List<Sobject> recordsToCreate = new List<Sobject>();
```java
List<SObject> recordsToCreate = new List<SObject>();
// Create 10 sample accounts
for(Integer accountCount = 0; accountCount < 10; accountCount++) {
// Create a new account & set a custom external ID text field called Uuid__c
Expand Down Expand Up @@ -54,22 +57,29 @@ System.assertEquals(1, Limits.getDmlStatements());
```

### Using a UUID's string value

If you already have a UUID as a string (previously generated in Apex, generated by an external system, etc), there are 3 static methods to help work with the string value.

1. **Do I have a valid UUID value?**

This checks if the string value matches the regex for a UUID v4, including hyphens (but it is case-insensitive)
```
Boolean isValid = Uuid.isValid(myUuidValue);
```
This checks if the string value matches the regex for a UUID v4, including hyphens (but it is case-insensitive)

```java
Boolean isValid = Uuid.isValid(myUuidValue);
```

2. **I have a UUID value but need to format it**

This returns a formatted string that follows the UUID pattern 8-4-4-4-12 in lowercase. If an invalid string is provided, a UuidException is thrown.
```
String formattedUuidValue = Uuid.formatValue(myUnformattedUuidValue);
```
This returns a formatted string that follows the UUID pattern 8-4-4-4-12 in lowercase. If an invalid string is provided, a UuidException is thrown.

```java
String formattedUuidValue = Uuid.formatValue(myUnformattedUuidValue);
```

3. **I have a UUID value, how can I use it to construct a UUID?**

This will automatically format the value for you, but the intial value must be a valid (unformatted) UUID string
```
Uuid myUuid = Uuid.valueOf(myUuidValue);
```
This will automatically format the value for you, but the intial value must be a valid (unformatted) UUID string

```java
Uuid myUuid = Uuid.valueOf(myUuidValue);
```
147 changes: 147 additions & 0 deletions apex-uuid/classes/Uuid.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/******************************************************************************************
* This file is part of the Apex UUID project, released under the MIT License. *
* See LICENSE file or go to https://github.com/jongpie/ApexUuid for full license details. *
******************************************************************************************/
public without sharing class Uuid {
private static final Integer HEX_BASE = HEX_CHARACTERS.length();
private static final String HEX_CHARACTERS = '0123456789abcdef';
private static final String HEX_PREFIX = '0x';
private static final List<String> HEX_CHARACTER_LIST = HEX_CHARACTERS.split(
''
);
private static final Integer UUID_V4_LENGTH = 36;
private static final String UUID_V4_REGEX = '[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}';

public static String formatValue(String unformattedValue) {
final String invalidValueError =
unformattedValue + ' is not a valid UUID value';

// Remove any non-alphanumeric characters
unformattedValue = unformattedValue.replaceAll('[^a-zA-Z0-9]', '');

// If the unformatted value isn't even the right length to be valid, then throw an exception
// Subtract 4 because the UUID_V4_LENGTH includes 4 '-' characters in the UUID pattern
if (unformattedValue.length() != (UUID_V4_LENGTH - 4))
throw new UuidException(invalidValueError);

// UUID Pattern: 8-4-4-4-12
String formattedValue =
unformattedValue.substring(0, 8) +
'-' +
unformattedValue.substring(8, 12) +
'-' +
unformattedValue.substring(12, 16) +
'-' +
unformattedValue.substring(16, 20) +
'-' +
unformattedValue.substring(20);

formattedValue = formattedValue.toLowerCase();

if (!Uuid.isValid(formattedValue))
throw new UuidException(invalidValueError);

return formattedValue;
}

public static Boolean isValid(String uuidValue) {
if (String.isBlank(uuidValue))
return false;
if (uuidValue.length() != UUID_V4_LENGTH)
return false;

Pattern uuidPattern = Pattern.compile(UUID_V4_REGEX.toLowerCase());
Matcher uuidMatcher = uuidPattern.matcher(uuidValue.toLowerCase());

return uuidMatcher.matches();
}

public static Uuid valueOf(String uuidValue) {
return new Uuid(uuidValue);
}

private final String value;

public Uuid() {
this.value = this.generateValue();
}

private Uuid(String uuidValue) {
this.value = Uuid.formatValue(uuidValue);
}

public String getValue() {
return this.value;
}

private String generateValue() {
String hexValue = EncodingUtil.convertToHex(Crypto.generateAesKey(128));

// Version Calculation: (i & 0x0f) | 0x40
// Version Format: Always begins with 4
String versionShiftedHexBits = this.getShiftedHexBits(
hexValue.substring(14, 16),
this.convertHexToInteger('0x0f'),
this.convertHexToInteger('0x40')
);

// Variant Calculation: (i & 0x3f) | 0x80
// Variant Format: Always begins with 8, 9, A or B
String variantShiftedHexBits = this.getShiftedHexBits(
hexValue.substring(18, 20),
this.convertHexToInteger('0x3f'),
this.convertHexToInteger('0x80')
);

String uuidValue =
hexValue.substring(0, 8) + // time-low
hexValue.substring(8, 12) + // time-mid
versionShiftedHexBits +
hexValue.substring(14, 16) + // time-high-and-version
variantShiftedHexBits +
hexValue.substring(18, 20) + // clock-seq-and-reserved + clock-seq-low
hexValue.substring(20); // node

return Uuid.formatValue(uuidValue);
}

private String getShiftedHexBits(
String hexSubstring,
Integer lowerThreshold,
Integer upperThreshold
) {
Integer shiftedIntegerBits =
(this.convertHexToInteger(hexSubstring) & lowerThreshold) |
upperThreshold;
return this.convertIntegerToHex(shiftedIntegerBits);
}

private Integer convertHexToInteger(String hexValue) {
hexValue = hexValue.toLowerCase();

if (hexValue.startsWith(HEX_PREFIX))
hexValue = hexValue.substringAfter(HEX_PREFIX);

Integer integerValue = 0;
for (String hexCharacter : hexValue.split('')) {
Integer hexCharacterIndex = HEX_CHARACTERS.indexOf(hexCharacter);

integerValue = HEX_BASE * integerValue + hexCharacterIndex;
}
return integerValue;
}

private String convertIntegerToHex(Integer integerValue) {
String hexValue = '';
while (integerValue > 0) {
Integer hexCharacterIndex = Math.mod(integerValue, HEX_BASE);

hexValue = HEX_CHARACTER_LIST[hexCharacterIndex] + hexValue;
integerValue = integerValue / HEX_BASE;
}
return hexValue;
}

private class UuidException extends Exception {
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" ?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>46.0</apiVersion>
<apiVersion>56.0</apiVersion>
<status>Active</status>
</ApexClass>
Loading

0 comments on commit 82077d6

Please sign in to comment.